diff --git a/NOTICE b/NOTICE
index a5a3ce72..181a2009 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,2 +1,2 @@
-BOP Community Bitcoin Server
-Copyright 2013 bits of proof zrt. http://bitsofproof.com
+BOP Bitcoin Server API
+Copyright 2013-2014 bits of proof zrt. https://bitsofproof.com
diff --git a/README.md b/README.md
index 01ce0dc0..8a8ef1ef 100644
--- a/README.md
+++ b/README.md
@@ -1,55 +1,24 @@
-BOP Community Bitcoin Server
-============================
+WARNING: ARCHIVE ONLY
+======================
-This software enables your application to send/receive Bitcoin payments or to data mine the network's transaction history, with ease.
+This repository contains early versions of Bits of Proof's software.
+Left at this place only to serve earlier references to it.
-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 BOP Bitcoin Server is our foundation, the first enterprise ready implementation of the Bitcoin protocol.
-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.
+Client applications connect with the server through a message bus, only embedding a small memory and network footprint library.
+Bitcoin keys that command your balances are never transmitted to the server as transactions are signed in the client library. Therefore it is safe to use a hosted and even shared instance of our servers, without compromising on security of your Bitcoin holdings.
+Our software implements features that go far beyond what is available in the ‘reference implementation’.
-Bits of Proof offers professionally hosted server instances for commercial use, that share and build on this code base:
+Bits of Proof built several payment processors, the first externally auditable exchange with multi signature P2SH wallets https://bullionbitcoin.com, and the most secure web wallet where you hold your keys literally in your hands: https://mytrezor.com
-* 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.
+CURRENT VERSION
+---------------
-* 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.
+A current version of the BOP Bitcoin Server API is available at:
-* BeBop - Simple but powerful mobile wallet best integrated with BopShop.
+https://github.com/bitsofproof/bop-bitcoin-client
-_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_
+Contact sales@bitsofproof.com for further information on our products.
-Build the Community Server
---------------------------
-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
-
- cd supernode
-
- mvn package
-
-Run
----
-
-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
-
-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
-
-
-License
--------
-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:
-
-1EuamejAs2Lcz1ZPNrEhLsFTLnEY29BYKU
-
-Donations will finance social events of Bits of Proof developer.
diff --git a/api/generateBCSAPIkeys b/api/generateBCSAPIkeys
deleted file mode 100755
index be2badd3..00000000
--- a/api/generateBCSAPIkeys
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/bash
-
-if [ -f BCSAPIPASSWORD ]
-then
- BCSAPIPASSWORD=`cat BCSAPIPASSWORD`
-else
- echo -n "BCSAPIPASSWORD: "
- read BCSAPIPASSWORD
- echo $BCSAPIPASSWORD >BCSAPIPASSWORD
-fi
-
-
-echo generating server key...
-rm -f bcsapi_server.keystore bcsapi_client.keystore jms_server.keystore
-echo generating key...
-keytool -genkey -validity 3650 -alias bitsofproofServer -dname "cn=bitsofproof supernode server, ou=IT, o=bitsofproof, c=NET" -keystore bcsapi_server.keystore -keypass $BCSAPIPASSWORD -storepass $BCSAPIPASSWORD
-echo extracting certificate...
-keytool -exportcert -alias bitsofproofServer -keystore bcsapi_server.keystore -rfc -file server.cert -keypass $BCSAPIPASSWORD -storepass $BCSAPIPASSWORD
-
-echo
-echo generatng client key...
-keytool -genkey -validity 3650 -alias bitsofproofClient -dname "cn=bitsofproof supernode client, ou=IT, o=bitsofproof, c=NET" -keystore bcsapi_client.keystore -keypass $BCSAPIPASSWORD -storepass $BCSAPIPASSWORD
-echo extracting certificate...
-keytool -exportcert -alias bitsofproofClient -keystore bcsapi_client.keystore -rfc -file client.cert -keypass $BCSAPIPASSWORD -storepass $BCSAPIPASSWORD
-
-echo
-echo generatng JMS server key...
-keytool -genkey -validity 3650 -alias bitsofproofJMSServer -dname "cn=bitsofproof supernode JMS server, ou=IT, o=bitsofproof, c=NET" -keystore jms_server.keystore -keypass $BCSAPIPASSWORD -storepass $BCSAPIPASSWORD
-echo extracting certificate...
-keytool -exportcert -alias bitsofproofJMSServer -keystore jms_server.keystore -rfc -file jmsserver.cert -keypass $BCSAPIPASSWORD -storepass $BCSAPIPASSWORD
-
-echo
-echo add client and server certs to JMS server key store ...
-keytool -importcert -noprompt -trustcacerts -alias bitsofproofServer -file server.cert -keystore jms_server.keystore -keypass $BCSAPIPASSWORD -storepass $BCSAPIPASSWORD
-keytool -importcert -noprompt -trustcacerts -alias bitsofproofClient -file client.cert -keystore jms_server.keystore -keypass $BCSAPIPASSWORD -storepass $BCSAPIPASSWORD
-
-echo
-echo add server cert to bcsapi_client.keystore...
-keytool -importcert -noprompt -trustcacerts -alias bitsofproofServer -file server.cert -keystore bcsapi_client.keystore -keypass $BCSAPIPASSWORD -storepass $BCSAPIPASSWORD
-keytool -importcert -noprompt -trustcacerts -alias bitsofproofJMSServer -file jmsserver.cert -keystore bcsapi_client.keystore -keypass $BCSAPIPASSWORD -storepass $BCSAPIPASSWORD
-echo add client cert to bcsapi_server.keystore...
-keytool -importcert -noprompt -trustcacerts -alias bitsofproofClient -file client.cert -keystore bcsapi_server.keystore -keypass $BCSAPIPASSWORD -storepass $BCSAPIPASSWORD
-keytool -importcert -noprompt -trustcacerts -alias bitsofproofJMSServer -file jmsserver.cert -keystore bcsapi_server.keystore -keypass $BCSAPIPASSWORD -storepass $BCSAPIPASSWORD
-echo
-rm -f server.cert client.cert jmsserver.cert
-
-
-
-echo done.
diff --git a/api/pom.xml b/api/pom.xml
index e23796af..0ce9edc2 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -8,7 +8,7 @@
com.bitsofproof
supernode
- 1.3.4-SNAPSHOT
+ 2.0.6-SNAPSHOT
../
@@ -17,7 +17,7 @@
com.google.protobuf.tools
maven-protoc-plugin
- 0.1.10
+ 0.1.10
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 56%
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 b968fb8f..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,34 +1,136 @@
-/*
- * 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
{
- 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/api/BCSAPI.java b/api/src/main/java/com/bitsofproof/supernode/api/BCSAPI.java
index aa169874..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,10 +15,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,7 +134,8 @@ public interface BCSAPI
* @param listener
* @throws BCSAPIException
*/
- public void scanTransactions (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException;
+ public void scanTransactionsForAddresses (Set addresses, UpdateMode mode, long after, TransactionListener listener)
+ throws BCSAPIException;
/**
* Scan transactions for an account
@@ -147,13 +148,13 @@ public interface BCSAPI
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.
+ * Scan unspent transactions using generic binary match.
*
* @param match
* @param listener
* @throws BCSAPIException
*/
- public void scanUTXO (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException;
+ public void scanUTXOForAddresses (Set addresses, 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 3bb58c03..6f9c3411 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;
@@ -313,6 +314,27 @@ public boolean isProduction () throws BCSAPIException
}
@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);
+ }
+
public void scanUTXO (Collection match, UpdateMode mode, long after, TransactionListener listener) throws BCSAPIException
{
scanRequest (match, mode, after, listener, "utxoMatchRequest");
@@ -324,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/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..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,12 +16,9 @@
package com.bitsofproof.supernode.api;
import java.io.Serializable;
-import java.util.List;
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;
@@ -103,33 +100,9 @@ public static TransactionOutput fromWire (WireFormat.Reader reader)
return o;
}
- public byte[] getOutputAddress ()
+ public Address getOutputAddress ()
{
- if ( ScriptFormat.isPayToAddress (script) )
- {
- List tokens;
- try
- {
- tokens = ScriptFormat.parse (script);
- return tokens.get (2).data;
- }
- catch ( ValidationException e )
- {
- }
- }
- else if ( ScriptFormat.isPayToScriptHash (script) )
- {
- List tokens;
- try
- {
- tokens = ScriptFormat.parse (script);
- return 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 ba9332fc..c20d3508 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
{
@@ -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 (AddressConverter.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)
@@ -91,7 +88,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)
@@ -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/ByteVector.java b/api/src/main/java/com/bitsofproof/supernode/common/ByteVector.java
index 1f1e5084..33d74b10 100644
--- a/api/src/main/java/com/bitsofproof/supernode/common/ByteVector.java
+++ b/api/src/main/java/com/bitsofproof/supernode/common/ByteVector.java
@@ -15,7 +15,7 @@
*/
package com.bitsofproof.supernode.common;
-import java.util.Arrays;
+import org.bouncycastle.util.Arrays;
public class ByteVector
{
@@ -30,7 +30,7 @@ public ByteVector (byte[] a)
public boolean equals (Object obj)
{
ByteVector other = (ByteVector) obj;
- return Arrays.equals (bytes, other.bytes);
+ return Arrays.areEqual (bytes, other.bytes);
}
@Override
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..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,22 +21,23 @@
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.wallet.Address;
+import com.bitsofproof.supernode.api.Address;
public class ECKeyPair implements Key
{
@@ -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/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/InMemoryBusConnectionFactory.java b/api/src/main/java/com/bitsofproof/supernode/common/InMemoryBusConnectionFactory.java
index 76e26ce8..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 ();
}
@@ -771,8 +738,6 @@ public Message receiveNoWait () throws JMSException
@Override
public synchronized void close () throws JMSException
{
- listener = null;
- queue = null;
}
}
@@ -800,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 );
@@ -962,6 +924,7 @@ public MessageConsumer createConsumer (Destination destination) throws JMSExcept
name = ((MockTemporaryQueue) destination).getQueueName ();
}
+ MockConsumer c = new MockConsumer ();
List cl;
synchronized ( consumer )
{
@@ -971,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
@@ -1047,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
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/common/ScriptFormat.java b/api/src/main/java/com/bitsofproof/supernode/common/ScriptFormat.java
index 2c03974e..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
@@ -29,6 +32,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
{
@@ -713,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
@@ -784,4 +815,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/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/AccountManager.java
index 93e77e06..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,11 +55,11 @@ 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) 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 +67,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
deleted file mode 100644
index c6c7cd48..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
- {
- UNKNOWN, COMMON, P2SH
- };
-
- private final Type type;
- private final byte[] bytes;
-
- private Network network = Network.UNKNOWN;;
-
- 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..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,20 +1,33 @@
+/*
+ * 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.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 +35,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 +61,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 +71,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 +87,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/BIP39.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java
new file mode 100644
index 00000000..8f46c34c
--- /dev/null
+++ b/api/src/main/java/com/bitsofproof/supernode/wallet/BIP39.java
@@ -0,0 +1,2200 @@
+/*
+ * 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.StringTokenizer;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+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[] 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);
+ for ( i = bits.length / 33 * 32; i < bits.length; ++i )
+ {
+ 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");
+ }
+ }
+ 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 String getMnemonic (byte[] data) throws ValidationException
+ {
+ if ( data.length % 4 != 0 )
+ {
+ throw new ValidationException ("Invalid data length for mnemonic");
+ }
+ byte[] check = Hash.sha256 (data);
+
+ boolean[] bits = new boolean[data.length * 8 + data.length / 4];
+
+ 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[i / 8] & (1 << (7 - (i % 8)))) > 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 (english[idx]);
+ if ( i < mlen - 1 )
+ {
+ mnemo.append (" ");
+ }
+ }
+ return mnemo.toString ();
+ }
+
+ private final static String[] english = {
+ "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/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java b/api/src/main/java/com/bitsofproof/supernode/wallet/BaseAccountManager.java
index c28be3a1..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;
@@ -17,11 +32,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 +99,7 @@ protected void reset ()
sending = createSendingUTXO ();
}
- public boolean isOwnAddress (byte[] address)
+ public boolean isOwnAddress (Address address)
{
return getKeyForAddress (address) != null;
}
@@ -162,7 +177,7 @@ protected Transaction createSpend (List sources, 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 +390,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 +406,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 +425,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 +436,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 +445,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 +456,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 )
@@ -541,20 +505,20 @@ public boolean updateWithTransaction (Transaction t)
if ( t.getBlockHash () != null )
{
confirmed.add (o);
- log.trace ("Settled " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ());
+ log.trace ("Settled " + t.getHash () + " [" + o.getIx () + "] (" + o.getOutputAddress () + ") " + o.getValue ());
}
else
{
if ( spend != null )
{
change.add (o);
- log.trace ("Change " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") "
+ log.trace ("Change " + t.getHash () + " [" + o.getIx () + "] (" + o.getOutputAddress () + ") "
+ o.getValue ());
}
else
{
receiving.add (o);
- log.trace ("Receiving " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") "
+ log.trace ("Receiving " + t.getHash () + " [" + o.getIx () + "] (" + o.getOutputAddress () + ") "
+ o.getValue ());
}
}
@@ -565,7 +529,7 @@ public boolean updateWithTransaction (Transaction t)
{
modified = true;
sending.add (o);
- log.trace ("Sending " + t.getHash () + " [" + o.getIx () + "] (" + ByteUtils.toHex (o.getOutputAddress ()) + ") " + o.getValue ());
+ log.trace ("Sending " + t.getHash () + " [" + o.getIx () + "] (" + o.getOutputAddress () + ") " + o.getValue ());
}
}
}
@@ -594,7 +558,7 @@ public boolean updateWithTransaction (Transaction t)
}
if ( out != null )
{
- log.trace ("Remove DS " + out.getTxHash () + " [" + out.getIx () + "] (" + ByteUtils.toHex (out.getOutputAddress ()) + ")"
+ log.trace ("Remove DS " + out.getTxHash () + " [" + out.getIx () + "] (" + out.getOutputAddress () + ")"
+ out.getValue ());
}
modified |= out != null;
@@ -698,6 +662,7 @@ protected void notifyListener (Transaction t)
}
catch ( Exception e )
{
+ log.error ("Uncaught exception in account listener", e);
}
}
}
@@ -757,25 +722,25 @@ public void dumpOutputs (PrintStream print)
public void dumpKeys (PrintStream print)
{
- Set 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 + ")");
}
}
}
@@ -784,7 +749,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/EncryptedHDRoot.java b/api/src/main/java/com/bitsofproof/supernode/wallet/EncryptedHDRoot.java
new file mode 100644
index 00000000..85a8796e
--- /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 static 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/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/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/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..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,21 +1,37 @@
+/*
+ * 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;
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 +40,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 +65,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 +73,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 +89,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/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..cccaa8fa
--- /dev/null
+++ b/api/src/main/java/com/bitsofproof/supernode/wallet/ShamirsSecretSharing.java
@@ -0,0 +1,152 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.List;
+
+import org.bouncycastle.util.Arrays;
+
+import com.bitsofproof.supernode.common.ValidationException;
+
+public class ShamirsSecretSharing
+{
+ public static class SecretShare
+ {
+ public byte x;
+ public byte dl;
+ public byte yl;
+ public byte[] y;
+ }
+
+ private final static SecureRandom random = new SecureRandom ();
+
+ 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
+ {
+ 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 + 3);
+ ++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.length];
+ random.nextBytes (r);
+ coeff[i] = new BigInteger (r);
+ }
+
+ SecretShare[] shares = new SecretShare[pieces];
+ for ( int x = 1; x <= pieces; ++x )
+ {
+ int pow = x;
+ BigInteger poly = new BigInteger (1, secret);
+ for ( int i = 0; i < needed - 1; ++i )
+ {
+ poly = poly.add (BigInteger.valueOf (pow).multiply (coeff[i]));
+ pow *= x;
+ }
+ shares[x - 1] = new SecretShare ();
+ 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 byte[] reconstruct (SecretShare[] shares) throws ValidationException
+ {
+ for ( int i = 0; i < shares.length - 1; ++i )
+ {
+ for ( int j = 0; j < shares.length; ++j )
+ {
+ if ( i != j && shares[i].x == shares[j].x )
+ {
+ throw new ValidationException ("Shares are not unique");
+ }
+ }
+ }
+ BigInteger[] y = new BigInteger[shares.length];
+ for ( int i = 0; i < shares.length; ++i )
+ {
+ y[i] = new BigInteger (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 =
+ 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);
+ }
+ }
+ 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/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;
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..2c5b255a
--- /dev/null
+++ b/api/src/test/java/com/bitsofproof/supernode/api/BIP39Test.java
@@ -0,0 +1,95 @@
+/*
+ * 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.SecureRandom;
+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.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 bip39TrezorTest () throws IOException, JSONException, ValidationException
+ {
+ 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)));
+ }
+ }
+
+ @Test
+ public void bip39EncodeDecodeTest () throws IOException, JSONException, ValidationException
+ {
+ 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 < 1000; ++i )
+ {
+ 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/EncryptedHDRootTest.java b/api/src/test/java/com/bitsofproof/supernode/api/EncryptedHDRootTest.java
new file mode 100644
index 00000000..bba6221e
--- /dev/null
+++ b/api/src/test/java/com/bitsofproof/supernode/api/EncryptedHDRootTest.java
@@ -0,0 +1,87 @@
+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")));
+ // 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)));
+ // 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)));
+ }
+ }
+}
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/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/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java b/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java
new file mode 100644
index 00000000..de936374
--- /dev/null
+++ b/api/src/test/java/com/bitsofproof/supernode/api/SahmirsSecretSharingTest.java
@@ -0,0 +1,97 @@
+package com.bitsofproof.supernode.api;
+
+import static org.junit.Assert.assertTrue;
+
+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;
+import com.bitsofproof.supernode.wallet.ShamirsSecretSharing;
+
+public class SahmirsSecretSharingTest
+{
+ @BeforeClass
+ public static void init ()
+ {
+ Security.addProvider (new BouncyCastleProvider ());
+ }
+
+ @Test
+ public void testBinary () throws ValidationException
+ {
+ SecureRandom random = new SecureRandom ();
+ for ( int i = 0; i < 100; ++i )
+ {
+ for ( int s = 3; s < 10; ++s )
+ {
+ for ( int g = 0; g < s; ++g )
+ {
+ 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));
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testMnemonic () throws ValidationException
+ {
+ SecureRandom random = new SecureRandom ();
+ for ( int i = 0; i < 10; ++i )
+ {
+ for ( int s = 3; s < 10; ++s )
+ {
+ for ( int g = 0; g < s; ++g )
+ {
+ for ( int n = 2; n < (s - g); ++n )
+ {
+ 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 )
+ {
+ 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));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/api/src/test/resources/BIP39.json b/api/src/test/resources/BIP39.json
new file mode 100644
index 00000000..9b536a2c
--- /dev/null
+++ b/api/src/test/resources/BIP39.json
@@ -0,0 +1,100 @@
+{
+ "english": [
+ [
+ "00000000000000000000000000000000",
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
+ ],
+ [
+ "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
+ "legal winner thank year wave sausage worth useful legal winner thank yellow",
+ ],
+ [
+ "80808080808080808080808080808080",
+ "letter advice cage absurd amount doctor acoustic avoid letter advice cage above",
+ ],
+ [
+ "ffffffffffffffffffffffffffffffff",
+ "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",
+ ],
+ [
+ "000000000000000000000000000000000000000000000000",
+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
+ ],
+ [
+ "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
+ "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will",
+ ],
+ [
+ "808080808080808080808080808080808080808080808080",
+ "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always",
+ ],
+ [
+ "ffffffffffffffffffffffffffffffffffffffffffffffff",
+ "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when",
+ ],
+ [
+ "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",
+ ],
+ [
+ "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",
+ ],
+ [
+ "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",
+ ],
+ [
+ "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",
+ ],
+ [
+ "1083fa24dbb0afa4e7b327d23f666567",
+ "awesome cabin matrix resist april sponsor paddle gossip split will off soon",
+ ],
+ [
+ "d8cbcd1ac2153ecd74048480c2732f637d642b21f0dd40df",
+ "sugar fury effort loud fault grit source mountain liar bean slim shoulder stone better march brick dolphin zero",
+ ],
+ [
+ "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",
+ ],
+ [
+ "f5e82717078a6ddc538a03e825f91bed",
+ "vote donkey shift audit plug until evolve document trial cool eight swarm",
+ ],
+ [
+ "16b59b6a426f2f302f73049a32ab8572394278982212357a",
+ "birth proud surround luggage very object saddle gauge olive next throw tongue neither detail gauge drastic cube strategy",
+ ],
+ [
+ "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",
+ ],
+ [
+ "7f93397f750f70a26513de2732ed95ee",
+ "legend oil garlic tube warfare eye nephew knock cheese number grace tackle",
+ ],
+ [
+ "14c29fe840dd1c9f05d392ba13e4e1466b32ed0726a15f89",
+ "below belt wheel like spike exhibit blanket inch ring palace debate mimic rebel isolate broken stage garbage enhance",
+ ],
+ [
+ "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",
+ ],
+ [
+ "a30b50a5439dcd1774f412ea5ec33403",
+ "perfect fold citizen mango system merry stable liquid tumble voyage snack alter",
+ ],
+ [
+ "70044da2175ad681d0ebbf2da83cf407eb9c8fd91fc0a8c9",
+ "hybrid carbon hammer concert pulp domain dry jewel color draft dial average right elevator good way potato energy",
+ ],
+ [
+ "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",
+ ]
+ ]
+}
\ No newline at end of file
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
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 a252844e..9f94fe27 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
com.bitsofproof
supernode
- 1.3.4-SNAPSHOT
+ 2.0.6-SNAPSHOT
supernode
The bitsofproof supernode implementation of the Bitcoin protocol
pom
@@ -40,25 +40,16 @@
api
- server
UTF-8
- 3.1.2.RELEASE
- 1.16-bop-patch
- 1.0
+ UTF-8
+ 4.0.0.RELEASE
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
+ 1.50
2.4.1
- 4.3.0.Final
20090211
1.2
@@ -96,37 +87,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
@@ -135,50 +102,17 @@
@{project.version}
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 2.4
+
+
+ assembly/distribution.xml
+
+
+
-
-
-
-
- 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/.gitignore b/server/.gitignore
deleted file mode 100644
index a0f43e75..00000000
--- a/server/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-BCSAPIPASSWORD
-bcsapi_server.keystore
-/derby.log
-logs
-/test.wallet
diff --git a/server/bop-service.sh b/server/bop-service.sh
deleted file mode 100755
index 2fd74ca7..00000000
--- a/server/bop-service.sh
+++ /dev/null
@@ -1,156 +0,0 @@
-#!/bin/sh
-# Copyright 2013 bits of proof zrt.
-# ------------------------------------------------------------------------
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You 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.
-# ------------------------------------------------------------------------
-
-#BOP_USER="test"
-service=`basename "$0"`
-export JVM_FLAGS="-server -Xmx4g"
-export BOP_CONTEXTS="leveldb production"
-
-#
-# Discover the BOP_BASE from the location of this script.
-#
-if [ -z "$BOP_BASE" ] ; then
-
- ## resolve links - $0 may be a link to BOP's home
- PRG="$0"
- saveddir=`pwd`
-
- # need this for relative symlinks
- dirname_prg=`dirname "$PRG"`
- cd "$dirname_prg"
-
- while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '.*/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
- done
-
- BOP_BASE=`dirname "$PRG"`
- cd "$saveddir"
-
- # make it fully qualified
- BOP_BASE=`cd "$BOP_BASE/.." && pwd`
- export BOP_BASE
-
-fi
-
-PID_FILE="${BOP_BASE}/BOP.pid"
-
-status() {
- if [ -f "${PID_FILE}" ] ; then
- pid=`cat "${PID_FILE}"`
- # check to see if it's gone...
- ps -p ${pid} > /dev/null
- if [ $? -eq 0 ] ; then
- return 1
- else
- rm "${PID_FILE}"
- fi
- fi
- return 0
-}
-
-stop() {
- if [ -f "${PID_FILE}" ] ; then
- pid=`cat "${PID_FILE}"`
- kill $@ ${pid} > /dev/null
- fi
- for i in 1 2 3 4 5 ; do
- status
- if [ $? -eq 0 ] ; then
- return 0
- fi
- sleep 1
- done
- echo "Could not stop process ${pid}"
- return 1
-}
-
-start() {
-
- status
- if [ $? -eq 1 ] ; then
- echo "Already running."
- return 1
- fi
-
- if [ -z "$BOP_USER" -o `id -un` = "$BOP_USER" ] ; then
- 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.1.jar ${BOP_CONTEXTS} > /dev/null 2> /dev/null &
- fi
-
- echo $! > "${PID_FILE}"
-
- # check to see if stays up...
- sleep 1
- status
- if [ $? -eq 0 ] ; then
- echo "Could not start ${service}"
- return 1
- fi
- echo "${service} is now running (${pid})"
- return 0
-}
-
-case $1 in
- start)
- echo "Starting ${service}"
- start
- exit $?
- ;;
-
- force-stop)
- echo "Forcibly Stopping ${service}"
- stop -9
- exit $?
- ;;
-
- stop)
- echo "Gracefully Stopping ${service}"
- stop
- exit $?
- ;;
-
- restart)
- echo "Restarting ${service}"
- stop
- start
- exit $?
- ;;
-
- status)
- status
- if [ $? -eq 0 ] ; then
- echo "${service} is stopped"
- else
- echo "${service} is running (${pid})"
- fi
- exit 0
- ;;
-
- *)
- echo "Usage: $0 {start|stop|restart|force-stop|status}" >&2
- exit 2
- ;;
-esac
diff --git a/server/pom.xml b/server/pom.xml
deleted file mode 100644
index 5ffb0fe0..00000000
--- a/server/pom.xml
+++ /dev/null
@@ -1,128 +0,0 @@
-
- 4.0.0
- bitsofproof-server
- ${project.artifactId}
- The bitsofproof implementation of the Bitcoin protocol
- jar
-
-
- com.bitsofproof
- supernode
- 1.3.4-SNAPSHOT
- ../
-
-
-
-
- bitsofproof
- bop Enterprise Bitcoin Server Repository
- http://maven.bitsofproof.com/maven
-
-
- fusesource.nexus.snapshot
- FuseSource Community Snapshot Repository
- http://repo.fusesource.com/nexus/content/groups/public-snapshots
-
-
-
-
-
- com.bitsofproof
- bitsofproof-server-api
- ${project.version}
-
-
- org.bouncycastle
- bcprov-jdk15on
- ${bouncycastle.version}
-
-
- org.fusesource.leveldbjni
- leveldbjni-all
- ${leveldbjni.version}
-
-
- com.google.protobuf
- protobuf-java
- ${protobuf.version}
-
-
- org.springframework
- spring-context
- ${springframework.version}
-
-
- org.springframework
- spring-test
- ${springframework.version}
-
-
- org.apache.geronimo.specs
- geronimo-jms_1.1_spec
- 1.1
-
-
-
- org.apache.activemq
- activemq-client
- 5.8.0
-
-
-
- org.fusesource.stompjms
- stompjms-client
- ${stomp.version}
-
-
- org.json
- json
- ${json.version}
-
-
- commons-cli
- commons-cli
- ${commons-cli.version}
-
-
- junit
- junit
- 4.10
- test
-
-
- org.slf4j
- slf4j-log4j12
- ${slf4j.version}
-
-
-
-
-
- maven-jar-plugin
- 2.4
-
-
-
- true
- com.bitsofproof.supernode.main.Main
- lib/
-
-
-
-
-
- com.google.protobuf.tools
- maven-protoc-plugin
- 0.1.10
-
-
-
- compile
- testCompile
-
-
-
-
-
-
-
diff --git a/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java
deleted file mode 100644
index 53c014d4..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/AddressHandler.java
+++ /dev/null
@@ -1,69 +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.InetSocketAddress;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-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;
-
-public class AddressHandler implements BitcoinMessageListener
-{
- private static final Logger log = LoggerFactory.getLogger (AddressHandler.class);
- private final BitcoinNetwork network;
-
- public AddressHandler (final BitcoinNetwork network)
- {
- this.network = network;
- network.addListener ("addr", this);
- network.addListener ("getaddr", new BitcoinMessageListener ()
- {
- @Override
- public void process (GetAddrMessage m, BitcoinPeer peer)
- {
- AddrMessage am = (AddrMessage) peer.createMessage ("addr");
- for ( BitcoinPeer p : network.getConnectPeers () )
- {
- InetSocketAddress is = p.getAddress ();
- WireFormat.Address a = new WireFormat.Address ();
- a.address = is.getAddress ();
- a.port = is.getPort ();
- a.services = peer.getServices ();
- a.time = System.currentTimeMillis () / 1000;
- am.getAddresses ().add (a);
- }
- peer.send (am);
- log.trace ("sent in reply to getaddr " + am.getAddresses ().size () + " peers");
- }
- });
- }
-
- @Override
- 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 (peer.getAddress ());
- }
- }
-}
diff --git a/server/src/main/java/com/bitsofproof/supernode/core/AddressSeeder.java b/server/src/main/java/com/bitsofproof/supernode/core/AddressSeeder.java
deleted file mode 100644
index b16e5ee0..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/AddressSeeder.java
+++ /dev/null
@@ -1,89 +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.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.bitsofproof.supernode.common.WireFormat.Address;
-import com.bitsofproof.supernode.messages.AddrMessage;
-
-public class AddressSeeder implements Runnable
-{
- private static final Logger log = LoggerFactory.getLogger (AddressSeeder.class);
-
- private final BitcoinNetwork network;
- private int delay = 60;
- private int startDelay = 60;
-
- public AddressSeeder (BitcoinNetwork network)
- {
- this.network = network;
- network.scheduleJobWithFixedDelay (this, startDelay, delay, TimeUnit.SECONDS);
- }
-
- @Override
- public void run ()
- {
- BitcoinPeer[] peers = network.getConnectPeers ();
- if ( peers.length == 0 )
- {
- return;
- }
- BitcoinPeer pick = peers[(int) (Math.floor (Math.random () * peers.length))];
- List al = new ArrayList ();
- Address address = new Address ();
- address.address = pick.getAddress ().getAddress ();
- address.port = network.getPort ();
- address.services = pick.getServices ();
- address.time = System.currentTimeMillis () / 1000;
- al.add (address);
- for ( BitcoinPeer peer : peers )
- {
- if ( peer != pick )
- {
- AddrMessage m = new AddrMessage (peer);
- m.setAddresses (al);
- peer.send (m);
- }
- }
- log.trace ("Sent an address to peers " + pick.getAddress ().getAddress ());
- }
-
- public int getDelay ()
- {
- return delay;
- }
-
- public void setDelay (int delay)
- {
- this.delay = delay;
- }
-
- public int getStartDelay ()
- {
- return startDelay;
- }
-
- public void setStartDelay (int startDelay)
- {
- this.startDelay = startDelay;
- }
-}
diff --git a/server/src/main/java/com/bitsofproof/supernode/core/AlertHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/AlertHandler.java
deleted file mode 100644
index 9a0f0740..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/AlertHandler.java
+++ /dev/null
@@ -1,49 +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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.bitsofproof.supernode.messages.AlertMessage;
-import com.bitsofproof.supernode.messages.BitcoinMessageListener;
-
-public class AlertHandler implements BitcoinMessageListener
-{
- private static final Logger log = LoggerFactory.getLogger (AlertHandler.class);
-
- private final Chain chain;
-
- public AlertHandler (BitcoinNetwork network)
- {
- network.addListener ("alert", this);
- chain = network.getChain ();
- }
-
- @Override
- public void process (AlertMessage am, BitcoinPeer peer)
- {
- if ( am.isValidWithKey (chain.getAlertKey ()) )
- {
- log.warn ("ALERT: " + am.getPayload ());
- }
- else
- {
- log.trace ("Rejected unauthorized alert. Banning.");
- peer.ban ("Unauthorized alert");
- }
- }
-}
diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinNetwork.java b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinNetwork.java
deleted file mode 100644
index d9a52423..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinNetwork.java
+++ /dev/null
@@ -1,296 +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.io.IOException;
-import java.net.InetSocketAddress;
-import java.security.SecureRandom;
-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.TimeUnit;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.bitsofproof.supernode.messages.BitcoinMessageListener;
-
-public class BitcoinNetwork extends P2P
-{
- public BitcoinNetwork (int connections) throws IOException
- {
- super (connections);
- }
-
- private static final Logger log = LoggerFactory.getLogger (BitcoinNetwork.class);
-
- private Chain chain;
- private BlockStore store;
- private Discovery discovery;
- private long protocolVersion = 70001;
-
- private final long versionNonce = new SecureRandom ().nextLong ();
-
- private final Map, ArrayList> listener = Collections
- .synchronizedMap (new HashMap, ArrayList> ());
- private final Set connectedPeers = Collections.synchronizedSet (new HashSet ());
- private final List peerListener = Collections.synchronizedList (new ArrayList ());
-
- @Override
- public void start () throws IOException
- {
- int port = getPort ();
- if ( port == 0 )
- {
- port = chain.getPort ();
- }
- setPort (port);
-
- super.start ();
- }
-
- static class JobWrapper
- {
-
- private static final Map jobs = Collections.synchronizedMap (new HashMap ());
-
- static class WrappedRunnable implements Runnable
- {
- private final Long nr = new SecureRandom ().nextLong ();
- private final Runnable job;
- private final boolean single;
-
- WrappedRunnable (Runnable job, boolean single)
- {
- this.job = job;
- jobs.put (nr, this);
- this.single = single;
- }
-
- @Override
- public void run ()
- {
- if ( jobs.containsKey (nr) )
- {
- if ( single )
- {
- jobs.remove (nr);
- }
- try
- {
- job.run ();
- }
- catch ( Exception e )
- {
- log.debug ("Exception in scheduled job. The job will not be rescheduled.", e);
- jobs.remove (this);
- }
- }
- }
- }
-
- public WrappedRunnable wrap (Runnable job, boolean single)
- {
- return new WrappedRunnable (job, single);
- }
-
- public void cancelJob (long id)
- {
- jobs.remove (id);
- }
- }
-
- private static JobWrapper jobWrapper = new JobWrapper ();
-
- public long scheduleJob (final Runnable job, int delay, TimeUnit unit)
- {
- JobWrapper.WrappedRunnable runnable = jobWrapper.wrap (job, true);
- getScheduler ().schedule (runnable, delay, unit);
- return runnable.nr;
- }
-
- public long scheduleJobWithFixedDelay (Runnable job, int startDelay, int delay, TimeUnit unit)
- {
- JobWrapper.WrappedRunnable runnable = jobWrapper.wrap (job, false);
- getScheduler ().scheduleWithFixedDelay (runnable, startDelay, delay, unit);
- return runnable.nr;
- }
-
- public void cancelJob (long jobid)
- {
- jobWrapper.cancelJob (jobid);
- }
-
- public BitcoinPeer[] getConnectPeers ()
- {
- synchronized ( connectedPeers )
- {
- return connectedPeers.toArray (new BitcoinPeer[0]);
- }
- }
-
- public void addPeer (final BitcoinPeer peer)
- {
- connectedPeers.add (peer);
- synchronized ( listener )
- {
- for ( BitcoinMessageListener extends BitcoinPeer.Message> l : listener.keySet () )
- {
- for ( String type : listener.get (l) )
- {
- peer.addListener (type, l);
- }
- }
- }
-
- notifyPeerAdded (peer);
- }
-
- public void notifyPeerRemoved (final BitcoinPeer peer)
- {
- connectedPeers.remove (peer);
- for ( final BitcoinPeerListener listener : peerListener )
- {
- listener.remove (peer);
- }
- }
-
- private void notifyPeerAdded (final BitcoinPeer peer)
- {
- for ( final BitcoinPeerListener listener : peerListener )
- {
- listener.add (peer);
- }
- }
-
- public void addPeerListener (BitcoinPeerListener listener)
- {
- peerListener.add (listener);
- }
-
- public void removePeerListener (BitcoinPeerListener listener)
- {
- peerListener.remove (listener);
- }
-
- public boolean isConnected (Peer peer)
- {
- return connectedPeers.contains (peer);
- }
-
- public void addListener (final String type, final BitcoinMessageListener extends BitcoinPeer.Message> l)
- {
- synchronized ( connectedPeers )
- {
- for ( BitcoinPeer peer : connectedPeers )
- {
- peer.addListener (type, l);
- }
- }
- // store listener that should be added to new node
- ArrayList listenedTypes;
- if ( (listenedTypes = listener.get (l)) == null )
- {
- listenedTypes = new ArrayList ();
- listener.put (l, listenedTypes);
- }
- if ( !listenedTypes.contains (type) )
- {
- listenedTypes.add (type);
- }
- }
-
- public void removeListener (final String type, final BitcoinMessageListener extends BitcoinPeer.Message> l)
- {
- synchronized ( connectedPeers )
- {
- for ( BitcoinPeer peer : connectedPeers )
- {
- peer.removeListener (type, l);
- }
- }
- listener.get (l).remove (type);
- }
-
- @Override
- public Peer createPeer (InetSocketAddress address, boolean outgoing)
- {
- BitcoinPeer peer = new BitcoinPeer (this, address, outgoing);
- return peer;
- }
-
- public Chain getChain ()
- {
- return chain;
- }
-
- public BlockStore getStore ()
- {
- return store;
- }
-
- public void setChain (Chain chain)
- {
- this.chain = chain;
- }
-
- public void setStore (BlockStore store)
- {
- this.store = store;
- }
-
- public long getVersionNonce ()
- {
- return versionNonce;
- }
-
- public long getChainHeight ()
- {
- return store.getChainHeight ();
- }
-
- @Override
- protected boolean discover ()
- {
- List al = discovery.discover ();
-
- for ( InetSocketAddress a : al )
- {
- addPeer (a);
- }
-
- return !al.isEmpty ();
- }
-
- public void setDiscovery (Discovery discovery)
- {
- this.discovery = discovery;
- }
-
- public long getProtocolVersion ()
- {
- return protocolVersion;
- }
-
- public void setProtocolVersion (long protocolVersion)
- {
- this.protocolVersion = 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
deleted file mode 100644
index 8ad38455..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeer.java
+++ /dev/null
@@ -1,476 +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.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-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;
-import com.bitsofproof.supernode.messages.AddrMessage;
-import com.bitsofproof.supernode.messages.AlertMessage;
-import com.bitsofproof.supernode.messages.BitcoinMessageListener;
-import com.bitsofproof.supernode.messages.BlockMessage;
-import com.bitsofproof.supernode.messages.FilterAddMessage;
-import com.bitsofproof.supernode.messages.FilterClearMessage;
-import com.bitsofproof.supernode.messages.FilterLoadMessage;
-import com.bitsofproof.supernode.messages.GetAddrMessage;
-import com.bitsofproof.supernode.messages.GetBlocksMessage;
-import com.bitsofproof.supernode.messages.GetDataMessage;
-import com.bitsofproof.supernode.messages.GetHeadersMessage;
-import com.bitsofproof.supernode.messages.HeadersMessage;
-import com.bitsofproof.supernode.messages.InvMessage;
-import com.bitsofproof.supernode.messages.MempoolMessage;
-import com.bitsofproof.supernode.messages.MerkleBlockMessage;
-import com.bitsofproof.supernode.messages.PingMessage;
-import com.bitsofproof.supernode.messages.PongMessage;
-import com.bitsofproof.supernode.messages.TxMessage;
-import com.bitsofproof.supernode.messages.VersionMessage;
-
-public class BitcoinPeer extends P2P.Peer
-{
- private static final Logger log = LoggerFactory.getLogger (BitcoinPeer.class);
-
- private static final long BANTIME = 24 * 60 * 60;
-
- private final BitcoinNetwork network;
-
- private String agent;
- private long height;
- private long peerVersion;
- private long peerServices;
- private final boolean outgoing;
- private long lastSpoken;
- private long trafficIn;
- private long trafficOut;
- private long errors;
- private BloomFilter filter;
- private boolean relay;
-
- private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool (1);
- private static final long CONNECTIONTIMEOUT = 30;
-
- public class Message implements P2P.Message
- {
- private final String command;
- private long version;
-
- public Message (String command)
- {
- this.command = command;
- version = peerVersion;
- }
-
- public long getVersion ()
- {
- return version;
- }
-
- public void setVersion (long version)
- {
- this.version = version;
- }
-
- public String getCommand ()
- {
- return command;
- }
-
- @Override
- public byte[] toByteArray ()
- {
- ByteArrayOutputStream out = new ByteArrayOutputStream ();
- WireFormat.Writer writer = new WireFormat.Writer (out);
- writer.writeUint32 (network.getChain ().getMagic ());
- writer.writeZeroDelimitedString (getCommand (), 12);
- WireFormat.Writer payload = new WireFormat.Writer ();
- toWire (payload);
- byte[] data = payload.toByteArray ();
- writer.writeUint32 (data.length);
-
- byte[] checksum = new byte[4];
- System.arraycopy (Hash.hash (data), 0, checksum, 0, 4);
- writer.writeBytes (checksum);
-
- writer.writeBytes (data);
- byte[] a = writer.toByteArray ();
- trafficOut += a.length;
- return a;
- }
-
- public void toWire (WireFormat.Writer writer)
- {
- };
-
- public void fromWire (WireFormat.Reader reader)
- {
- };
- }
-
- public Message createMessage (String command)
- {
- if ( command.equals ("version") )
- {
- return new VersionMessage (this);
- }
- else if ( command.equals ("inv") )
- {
- return new InvMessage (this);
- }
- else if ( command.equals ("addr") )
- {
- return new AddrMessage (this);
- }
- else if ( command.equals ("getdata") )
- {
- return new GetDataMessage (this);
- }
- else if ( command.equals ("getblocks") )
- {
- return new GetBlocksMessage (this);
- }
- else if ( command.equals ("getheaders") )
- {
- return new GetHeadersMessage (this);
- }
- else if ( command.equals ("block") )
- {
- return new BlockMessage (this);
- }
- else if ( command.equals ("merkleblock") )
- {
- return new MerkleBlockMessage (this);
- }
- else if ( command.equals ("headers") )
- {
- return new HeadersMessage (this);
- }
- else if ( command.equals ("alert") )
- {
- return new AlertMessage (this);
- }
- else if ( command.equals ("ping") )
- {
- return new PingMessage (this);
- }
- else if ( command.equals ("pong") )
- {
- return new PongMessage (this);
- }
- else if ( command.equals ("tx") )
- {
- return new TxMessage (this);
- }
- else if ( command.equals ("mempool") )
- {
- return new MempoolMessage (this);
- }
- else if ( command.equals ("verack") )
- {
- return new Message (command);
- }
- else if ( command.equals ("getaddr") )
- {
- return new GetAddrMessage (this);
- }
- else if ( command.equals ("filterload") )
- {
- return new FilterLoadMessage (this);
- }
- else if ( command.equals ("filteradd") )
- {
- return new FilterAddMessage (this);
- }
- else if ( command.equals ("filterclear") )
- {
- return new FilterClearMessage (this);
- }
- else if ( command.equals ("notfound") )
- {
- return new Message (command);
- }
- log.trace ("Peer sent unknown message: " + command + " " + getAddress ());
- return new Message (command);
- }
-
- public boolean isRelay ()
- {
- return relay;
- }
-
- public long getLastSpoken ()
- {
- return lastSpoken;
- }
-
- public BloomFilter getFilter ()
- {
- return filter;
- }
-
- public void setFilter (BloomFilter filter)
- {
- this.filter = filter;
- }
-
- private final Map>> listener = Collections
- .synchronizedMap (new HashMap>> ());
-
- protected BitcoinPeer (P2P p2p, final InetSocketAddress address, boolean out)
- {
- p2p.super (address);
- network = (BitcoinNetwork) p2p;
- this.outgoing = out;
-
- // this will be overwritten by the first version message we get
- peerVersion = network.getProtocolVersion ();
-
- addListener ("version", new BitcoinMessageListener ()
- {
- @Override
- public void process (VersionMessage v, BitcoinPeer peer)
- {
- if ( v.getNonce () == network.getVersionNonce () )
- {
- disconnect (0, Integer.MAX_VALUE, "myself"); // connect to self
- }
- else
- {
- agent = v.getAgent ();
- height = v.getHeight ();
- peerVersion = Math.min (peerVersion, v.getVersion ());
- peerServices = v.getServices ();
- relay = v.isRelay ();
- if ( !outgoing )
- {
- onConnect ();
- }
- peer.send (peer.createMessage ("verack"));
- }
- }
- });
-
- addListener ("verack", new BitcoinMessageListener ()
- {
- @Override
- public void process (BitcoinPeer.Message m, BitcoinPeer peer)
- {
- log.trace ("got verack from " + getAddress ());
- log.info ("Connection to '" + getAgent () + "' [" + peerVersion + "] at " + getAddress () + " Open connections: "
- + getNetwork ().getNumberOfConnections ());
- network.addPeer (peer);
- peer.send (peer.createMessage ("getaddr"));
- if ( network.getChain ().isSlave () )
- {
- MempoolMessage mp = (MempoolMessage) peer.createMessage ("mempool");
- peer.send (mp);
- }
- }
- });
- }
-
- public BitcoinNetwork getNetwork ()
- {
- return network;
- }
-
- public long getVersion ()
- {
- return peerVersion;
- }
-
- public long getServices ()
- {
- return peerServices;
- }
-
- public long getHeight ()
- {
- return height;
- }
-
- public String getAgent ()
- {
- return agent;
- }
-
- public void error (String reason, int severity)
- {
- errors += severity;
- if ( errors >= 100 )
- {
- ban (reason);
- }
- }
-
- public void ban (String reason)
- {
- disconnect (0, BANTIME, reason);
- }
-
- @Override
- protected void onDisconnect (long timeout, long bannedForSeconds, String reason)
- {
- network.notifyPeerRemoved (this);
- log.info ("Disconnected '" + getAgent () + "' at " + getAddress ());
- }
-
- private static final int MAX_BLOCK_SIZE = 1000000 + 80;
-
- @Override
- protected Message parse (InputStream readIn) throws IOException
- {
- try
- {
- byte[] head = new byte[24];
- if ( readIn.read (head) != head.length )
- {
- throw new ValidationException ("Read timeout for " + getAddress ());
- }
- WireFormat.Reader reader = new WireFormat.Reader (head);
- long mag = reader.readUint32 ();
- if ( mag != network.getChain ().getMagic () )
- {
- log.trace ("Peer talks to thw wrong chain. Banned.");
- ban ("Talked to the wrong chain");
- throw new ValidationException ("Wrong magic for this chain " + getAddress ());
- }
-
- String command = reader.readZeroDelimitedString (12);
- Message m = createMessage (command);
- long length = reader.readUint32 ();
- byte[] checksum = reader.readBytes (4);
- if ( length < 0 || length >= MAX_BLOCK_SIZE )
- {
- log.trace ("Peer sends oversize message. Banned.");
- ban ("Sent oversize message");
- throw new ValidationException ("Block size limit exceeded " + getAddress ());
- }
- else
- {
- byte[] buf = new byte[(int) length];
- if ( readIn.read (buf) != buf.length )
- {
- throw new ValidationException ("Package length mismatch " + getAddress ());
- }
- byte[] cs = new byte[4];
- System.arraycopy (Hash.hash (buf), 0, cs, 0, 4);
- if ( !Arrays.equals (cs, checksum) )
- {
- ban ("Message checksum mismatch");
- throw new ValidationException ("Checksum mismatch " + getAddress ());
- }
-
- m.fromWire (new WireFormat.Reader (buf));
- }
- if ( command.equals ("inv") || command.equals ("tx") || command.equals ("block") || command.equals ("getblocks") || command.equals ("getheaders")
- || command.equals ("getdata") )
- {
- lastSpoken = System.currentTimeMillis () / 1000;
- }
- trafficIn += length;
- return m;
- }
- catch ( ValidationException e )
- {
- throw new IOException (e);
- }
- }
-
- @Override
- protected void onConnect ()
- {
- VersionMessage m = (VersionMessage) createMessage ("version");
- m.setHeight (network.getChainHeight ());
- m.setPeer (getAddress ().getAddress ());
- m.setRemotePort (getAddress ().getPort ());
- send (m);
- log.trace ("Sent version to " + getAddress ());
- final BitcoinPeer peer = this;
- scheduler.schedule (new Runnable ()
- {
- @Override
- public void run ()
- {
- if ( !network.isConnected (peer) )
- {
- peer.disconnect ();
- }
- }
- }, CONNECTIONTIMEOUT, TimeUnit.SECONDS);
- }
-
- @Override
- protected boolean isHandshakeSuccessful ()
- {
- return network.isConnected (this);
- }
-
- @Override
- @SuppressWarnings ({ "rawtypes", "unchecked" })
- protected void receive (P2P.Message m)
- {
- final BitcoinPeer self = this;
- final BitcoinPeer.Message bm = (Message) m;
- List> classListener = listener.get (bm.getCommand ());
- if ( classListener != null )
- {
- for ( BitcoinMessageListener l : classListener )
- {
- l.process (bm, self);
- }
- }
- }
-
- protected void addListener (String type, BitcoinMessageListener extends BitcoinPeer.Message> l)
- {
- ArrayList> ll = listener.get (type);
- if ( ll == null )
- {
- ll = new ArrayList> ();
- listener.put (type, ll);
- }
- if ( !ll.contains (l) )
- {
- ll.add (l);
- }
- }
-
- protected void removeListener (String type, BitcoinMessageListener extends BitcoinPeer.Message> l)
- {
- ArrayList> ll = listener.get (type);
- if ( ll != null )
- {
- ll.remove (l);
- }
- }
-}
diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeerListener.java b/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeerListener.java
deleted file mode 100644
index ab1af40c..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/BitcoinPeerListener.java
+++ /dev/null
@@ -1,23 +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;
-
-public interface BitcoinPeerListener
-{
- void remove (BitcoinPeer peer);
-
- void add (BitcoinPeer peer);
-}
diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java
deleted file mode 100644
index 8c5a74c2..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/BlockStore.java
+++ /dev/null
@@ -1,86 +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.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;
-import com.bitsofproof.supernode.common.ValidationException;
-import com.bitsofproof.supernode.model.Blk;
-import com.bitsofproof.supernode.model.Tx;
-
-public interface BlockStore
-{
- public interface CacheContextRunnable
- {
- public void run (TxOutCache cache) throws ValidationException;
- }
-
- public interface TransactionProcessor
- {
- public void process (Tx transaction);
- }
-
- public void cache (Chain chain, int cacheSize) throws ValidationException;
-
- public ValidationException runInCacheContext (CacheContextRunnable runnable);
-
- public void addTrunkListener (TrunkListener l);
-
- public long getPeriodLength (String previousHash, int reviewPeriod);
-
- public List getLocator ();
-
- public void storeBlock (Blk b) throws ValidationException;
-
- public String getHeadHash ();
-
- public void resetStore (Chain chain) throws ValidationException;
-
- public Blk getBlock (String hash) throws ValidationException;
-
- public Blk getBlockHeader (String hash) throws ValidationException;
-
- public Tx getTransaction (String hash) throws ValidationException;
-
- public boolean isStoredBlock (String hash);
-
- public boolean validateTransaction (Tx tx, TxOutCache resolvedInputs) throws ValidationException;
-
- public void resolveTransactionInputs (Tx tx, TxOutCache resolvedInputs) throws ValidationException;
-
- public long getChainHeight ();
-
- public int getBlockHeight (String hash);
-
- public boolean isEmpty ();
-
- 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;
-
- 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/BlocktesterChain.java b/server/src/main/java/com/bitsofproof/supernode/core/BlocktesterChain.java
deleted file mode 100644
index 8a5700cb..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/BlocktesterChain.java
+++ /dev/null
@@ -1,172 +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.math.BigInteger;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.bouncycastle.util.encoders.Hex;
-
-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;
-import com.bitsofproof.supernode.model.TxOut;
-
-public class BlocktesterChain implements Chain
-{
- static final byte[] SATOSHI_KEY = Hex
- .decode ("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284");
-
- static final BigInteger minTarget = BigInteger.valueOf (1).shiftLeft (256).subtract (BigInteger.ONE);
-
- @Override
- public boolean isSlave ()
- {
- return false;
- }
-
- @Override
- public boolean checkBeforeCheckpoint ()
- {
- return true;
- }
-
- @Override
- public boolean allowImmediateCoinbaseSpend ()
- {
- return false;
- }
-
- @Override
- public BigInteger getMinimumTarget ()
- {
- return minTarget;
- }
-
- @Override
- public long getMagic ()
- {
- return 0xDAB5BFFAL;
- }
-
- @Override
- public int getPort ()
- {
- return 8333;
- }
-
- @Override
- public long getRewardForHeight (int height)
- {
- return (50L * Tx.COIN) >> (height / 150L);
- }
-
- @Override
- public int getDifficultyReviewBlocks ()
- {
- return 150;
- }
-
- @Override
- public int getTargetBlockTime ()
- {
- return 1209600;
- }
-
- @Override
- public byte[] getAlertKey ()
- {
- return SATOSHI_KEY;
- }
-
- @Override
- public boolean isProduction ()
- {
- return true;
- }
-
- @Override
- public int getAddressFlag ()
- {
- return 0x00;
- }
-
- @Override
- public int getP2SHAddressFlag ()
- {
- return 0x05;
- }
-
- @Override
- public Blk getGenesis ()
- {
- Blk block = new Blk ();
-
- block.setChainWork (1);
- block.setHeight (0);
-
- block.setVersion (1);
- block.setCreateTime (1296688602);
- block.setDifficultyTarget (0x207fffff);
- block.setNonce (2);
- block.setPreviousHash (Hash.ZERO_HASH_STRING);
-
- List transactions = new ArrayList ();
- block.setTransactions (transactions);
- Tx t = new Tx ();
- transactions.add (t);
- t.setBlock (block);
- t.setVersion (1);
-
- List inputs = new ArrayList ();
- t.setInputs (inputs);
- TxIn input = new TxIn ();
- input.setTransaction (t);
- inputs.add (input);
- input.setSource (null);
- input.setSourceHash (Hash.ZERO_HASH_STRING);
- input.setIx (0L);
- input.setSequence (0xFFFFFFFFL);
- input.setScript (ByteUtils.fromHex ("04" + "ffff001d" + // difficulty target
- "010445" +
- // "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"
- "5468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73"));
-
- List outputs = new ArrayList ();
- t.setOutputs (outputs);
- TxOut output = new TxOut ();
- output.setTransaction (t);
- outputs.add (output);
- output.setValue (5000000000L);
- output.setScript (Hex
- .decode ("4104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"));
- output.setTxHash (t.getHash ());
- output.setHeight (0);
- output.setAvailable (false); // make this explicit
- try
- {
- block.computeHash ();
- }
- catch ( ValidationException e )
- {
- }
- return block;
- }
-}
diff --git a/server/src/main/java/com/bitsofproof/supernode/core/BloomFilterHandler.java b/server/src/main/java/com/bitsofproof/supernode/core/BloomFilterHandler.java
deleted file mode 100644
index 0f552691..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/BloomFilterHandler.java
+++ /dev/null
@@ -1,63 +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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.bitsofproof.supernode.messages.BitcoinMessageListener;
-import com.bitsofproof.supernode.messages.FilterAddMessage;
-import com.bitsofproof.supernode.messages.FilterClearMessage;
-import com.bitsofproof.supernode.messages.FilterLoadMessage;
-
-public class BloomFilterHandler
-{
- private static final Logger log = LoggerFactory.getLogger (BloomFilterHandler.class);
-
- public BloomFilterHandler (final BitcoinNetwork network)
- {
- network.addListener ("filterclear", new BitcoinMessageListener ()
- {
- @Override
- public void process (FilterClearMessage m, BitcoinPeer peer)
- {
- peer.setFilter (null);
- log.trace ("cleared Bloom filter for " + peer.getAddress ());
- }
- });
- network.addListener ("filterload", new BitcoinMessageListener ()
- {
- @Override
- public void process (FilterLoadMessage m, BitcoinPeer peer)
- {
- peer.setFilter (m.getFilter ());
- log.trace ("loaded Bloom filter for " + peer.getAddress ());
- }
- });
- network.addListener ("filteradd", new BitcoinMessageListener ()
- {
- @Override
- public void process (FilterAddMessage m, BitcoinPeer peer)
- {
- if ( peer.getFilter () != null )
- {
- peer.getFilter ().add (m.getData ());
- log.trace ("update Bloom filter for " + peer.getAddress ());
- }
- }
- });
- }
-}
diff --git a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java b/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java
deleted file mode 100644
index 072ed0d5..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/CachedBlockStore.java
+++ /dev/null
@@ -1,1703 +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.math.BigInteger;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-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.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;
-import com.bitsofproof.supernode.common.ValidationException;
-import com.bitsofproof.supernode.common.WireFormat;
-import com.bitsofproof.supernode.model.Blk;
-import com.bitsofproof.supernode.model.Head;
-import com.bitsofproof.supernode.model.Tx;
-import com.bitsofproof.supernode.model.TxIn;
-import com.bitsofproof.supernode.model.TxOut;
-
-public abstract class CachedBlockStore implements BlockStore
-{
- private static final Logger log = LoggerFactory.getLogger (CachedBlockStore.class);
-
- private static final long MAX_BLOCK_SIGOPS = 20000;
-
- // not allowed to branch further back on trunk
- private static final int FORCE_TRUNK = 100;
- private static final long MIN_RELAY_TX_FEE = 10000;
- private static final long KB_RELAY_TX_FEE = 50000;
- private static final int COINBASE_MATURITY = 100;
-
- private static final Map checkPoints = new HashMap ();
- private static final int lastCheckPoint;
-
- static
- {
- checkPoints.put (11111, "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d");
- checkPoints.put (33333, "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6");
- checkPoints.put (74000, "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20");
- checkPoints.put (105000, "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97");
- checkPoints.put (134444, "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe");
- checkPoints.put (168000, "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763");
- checkPoints.put (193000, "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317");
- checkPoints.put (210000, "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e");
- checkPoints.put (222222, "00000000000000b8b49d0b61b14994b5c0a511c4b48a1e251ff2b479b2e6f678");
- checkPoints.put (232000, "000000000000018f47636e1c3a946db77624880ae484ffb0233f5aac6316b3bb");
- checkPoints.put (270000, "0000000000000002a775aec59dc6a9e4bb1c025cf1b8c2195dd9dc3998c827c5");
-
- lastCheckPoint = 270000;
- }
-
- protected Chain chain;
-
- private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock ();
-
- private boolean enforceV2Block = false;
-
- protected CachedHead currentHead = null;
- protected final Map cachedBlocks = new HashMap ();
- protected final Map cachedHeads = new HashMap ();
-
- private final LinkedList blockChain = new LinkedList ();
-
- private final ImplementTxOutCache cachedUTXO = new ImplementTxOutCache ();
- private final List trunkListener = new ArrayList ();
-
- private final ExecutorService inputProcessor = Executors.newFixedThreadPool (Runtime.getRuntime ().availableProcessors () * 2);
- private final ExecutorService transactionsProcessor = Executors.newFixedThreadPool (Runtime.getRuntime ().availableProcessors () * 2);
-
- @Override
- public ValidationException runInCacheContext (CacheContextRunnable runnable)
- {
- try
- {
- lock.readLock ().lock ();
-
- runnable.run (cachedUTXO);
- return null;
- }
- catch ( ValidationException e )
- {
- return e;
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- }
-
- protected abstract void clearStore ();
-
- protected abstract void startBatch ();
-
- protected abstract void endBatch ();
-
- protected abstract void cancelBatch ();
-
- protected abstract void cacheChain ();
-
- protected abstract void cacheHeads ();
-
- protected abstract void cacheColors ();
-
- protected abstract void cacheUTXO (int lookback, TxOutCache cache);
-
- protected abstract List findTxOuts (Map> need) throws ValidationException;
-
- protected abstract void checkBIP30Compliance (Set txs, int untilHeight) throws ValidationException;
-
- protected abstract void backwardCache (Blk b, TxOutCache cache, boolean modify) throws ValidationException;
-
- protected abstract void forwardCache (Blk b, TxOutCache cache, boolean modify) throws ValidationException;
-
- protected abstract TxOut getSourceReference (TxOut source) throws ValidationException;
-
- protected abstract void insertBlock (Blk b) throws ValidationException;
-
- protected abstract void insertHead (Head head);
-
- protected abstract Head updateHead (Head head);
-
- protected abstract Head retrieveHead (CachedHead cached) throws ValidationException;
-
- protected abstract Blk retrieveBlock (CachedBlock cached) throws ValidationException;
-
- protected abstract Blk retrieveBlockHeader (CachedBlock cached) throws ValidationException;
-
- protected abstract void updateColor (TxOut root, String fungibleName) throws ValidationException;
-
- @Override
- public void addTrunkListener (TrunkListener listener)
- {
- trunkListener.add (listener);
- }
-
- protected static class CachedHead
- {
- private Long id;
- private CachedBlock last;
- private long chainWork;
- private int height;
- private CachedHead previous;
- private int previousHeight;
- private final Set blocks = new HashSet ();
-
- public CachedHead ()
- {
- }
-
- public Long getId ()
- {
- return id;
- }
-
- public void setId (Long id)
- {
- this.id = id;
- }
-
- public long getChainWork ()
- {
- return chainWork;
- }
-
- public long getHeight ()
- {
- return height;
- }
-
- public void setChainWork (long chainWork)
- {
- this.chainWork = chainWork;
- }
-
- public void setHeight (int height)
- {
- this.height = height;
- }
-
- public Set getBlocks ()
- {
- return blocks;
- }
-
- public CachedHead getPrevious ()
- {
- return previous;
- }
-
- public void setPrevious (CachedHead previous)
- {
- this.previous = previous;
- }
-
- public CachedBlock getLast ()
- {
- return last;
- }
-
- public void setLast (CachedBlock last)
- {
- this.last = last;
- }
-
- public int getPreviousHeight ()
- {
- return previousHeight;
- }
-
- public void setPreviousHeight (int previousHeight)
- {
- this.previousHeight = previousHeight;
- }
-
- }
-
- protected static class CachedBlock
- {
- public CachedBlock (String hash, Long id, CachedBlock previous, long time, int height, int version, byte[] filterMap, int filterFunctions)
- {
- this.hash = hash;
- this.id = id;
- this.previous = previous;
- this.time = time;
- this.height = height;
- this.version = version;
- this.filterMap = filterMap;
- this.filterFunctions = filterFunctions;
- }
-
- private final String hash;
- private final Long id;
- private final CachedBlock previous;
- private final long time;
- private final int height;
- private final int version;
- private final byte[] filterMap;
- private final int filterFunctions;
-
- public byte[] getFilterMap ()
- {
- return filterMap;
- }
-
- public int getFilterFunctions ()
- {
- return filterFunctions;
- }
-
- public Long getId ()
- {
- return id;
- }
-
- public CachedBlock getPrevious ()
- {
- return previous;
- }
-
- public long getTime ()
- {
- return time;
- }
-
- public String getHash ()
- {
- return hash;
- }
-
- public int getHeight ()
- {
- return height;
- }
-
- public int getVersion ()
- {
- return version;
- }
-
- @Override
- public int hashCode ()
- {
- return hash.hashCode ();
- }
-
- @Override
- public boolean equals (Object o)
- {
- if ( o == null )
- {
- return false;
- }
- if ( o == this )
- {
- return true;
- }
- if ( o instanceof CachedBlock )
- {
- return hash.equals (((CachedBlock) o).hash);
- }
- return false;
- }
- }
-
- @Override
- public void cache (Chain chain, int size) throws ValidationException
- {
- this.chain = chain;
-
- try
- {
- lock.writeLock ().lock ();
- cachedBlocks.clear ();
- cachedHeads.clear ();
- blockChain.clear ();
- cachedUTXO.clear ();
-
- log.trace ("Cache heads...");
- cacheHeads ();
-
- log.trace ("Cache colors...");
- cacheColors ();
-
- log.trace ("Cache chain...");
- cacheChain ();
-
- updateBlockChainCache ();
-
- if ( size > 0 )
- {
- log.trace ("Cache UTXO set ...");
- cacheUTXO (size, cachedUTXO);
- }
-
- log.trace ("Cache filled.");
- }
- finally
- {
- lock.writeLock ().unlock ();
- }
- }
-
- @Override
- public void catchUp (String hash, List added, List removed) throws ValidationException
- {
- try
- {
- lock.readLock ().lock ();
-
- String trunk = hash;
- while ( !isOnTrunk (trunk) )
- {
- log.debug ("need to remove: " + trunk);
- CachedBlock b = cachedBlocks.get (trunk);
- removed.add (retrieveBlock (b));
- b = b.previous;
- trunk = b.hash;
- }
- CachedBlock q = currentHead.getLast ();
- while ( q != null )
- {
- if ( q.hash.equals (trunk) )
- {
- break;
- }
- log.debug ("need to add: " + q.hash);
- added.add (retrieveBlock (q));
- q = q.previous;
- }
- Collections.reverse (added);
- }
- finally
- {
- lock.readLock ().unlock ();
- }
-
- }
-
- private void updateBlockChainCache ()
- {
- List appendList = new ArrayList ();
- CachedBlock q = currentHead.getLast ();
- CachedBlock p = q.previous;
- while ( p != null )
- {
- if ( blockChain.isEmpty () )
- {
- appendList.add (q);
- }
- else if ( blockChain.getLast () != p )
- {
- blockChain.removeLast ();
- appendList.add (q);
- }
- else
- {
- appendList.add (q);
- break;
- }
- q = p;
- p = q.previous;
- }
- Collections.reverse (appendList);
- blockChain.addAll (appendList);
- }
-
- @Override
- 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 = firstIndex; i < firstIndex + lookAhead; ++i )
- {
- ByteVector address = new ByteVector (ek.getKey (i).getAddress ().toByteArray ());
- matchSet.add (address);
- addressSet.put (address, i);
- }
- try
- {
- Map> hashes = new HashMap> ();
- lock.readLock ().lock ();
-
- for ( CachedBlock cb : blockChain )
- {
- if ( after > 1231006505000L && cb.time * 1000 <= after || after < 1231006505000L && cb.height <= after )
- {
- continue;
- }
- 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 (utxo, matchSet, UpdateMode.all) )
- {
- 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 - firstIndex; ++i )
- {
- ByteVector address = new ByteVector (ek.getKey (i).getAddress ().toByteArray ());
- 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");
- }
-
- @Override
- public void filterTransactions (boolean utxo, Set matchSet, UpdateMode update, long after, TransactionProcessor processor)
- throws ValidationException
- {
- try
- {
- Map> hashes = new HashMap> ();
-
- lock.readLock ().lock ();
-
- for ( CachedBlock cb : blockChain )
- {
- if ( after > 1231006505000L && cb.time * 1000 <= after || after < 1231006505000L && cb.height <= after )
- {
- continue;
- }
- 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 (utxo, matchSet, update) )
- {
- processor.process (t);
- ++n;
- }
- }
- 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");
- }
-
- private boolean isBlockOnBranch (CachedBlock block, CachedHead branch, int untilHeight)
- {
- if ( branch.getBlocks ().contains (block) )
- {
- return block.getHeight () <= untilHeight;
- }
- if ( branch.getPrevious () == null )
- {
- return false;
- }
- return isBlockOnBranch (block, branch.getPrevious (), branch.getPreviousHeight ());
- }
-
- protected boolean isOnTrunk (String block)
- {
- try
- {
- lock.readLock ().lock ();
-
- CachedBlock b = cachedBlocks.get (block);
- return isBlockOnBranch (b, currentHead, (int) currentHead.getHeight ());
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- }
-
- private boolean isSuperMajority (int minVersion, CachedBlock from, int nRequired, int nToCheck)
- {
- int nFound = 0;
- for ( int i = 0; i < nToCheck && nFound < nRequired && from != null; i++ )
- {
- if ( from.version >= minVersion )
- {
- ++nFound;
- }
- from = from.previous;
- }
- return (nFound >= nRequired);
- }
-
- @Override
- public boolean isStoredBlock (String hash)
- {
- try
- {
- lock.readLock ().lock ();
-
- return cachedBlocks.get (hash) != null;
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- }
-
- @Override
- public long getChainHeight ()
- {
- try
- {
- lock.readLock ().lock ();
-
- return currentHead.getHeight ();
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- }
-
- @Override
- public int getBlockHeight (String hash)
- {
- try
- {
- lock.readLock ().lock ();
-
- CachedBlock b = cachedBlocks.get (hash);
- return b != null ? b.height : -1;
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- }
-
- @Override
- public List getInventory (List locator, String last, int limit)
- {
- try
- {
- lock.readLock ().lock ();
-
- List inventory = new LinkedList ();
- CachedBlock curr = currentHead.getLast ();
- CachedBlock prev = curr.getPrevious ();
- if ( !last.equals (Hash.ZERO_HASH.toString ()) )
- {
- while ( prev != null && !curr.getHash ().equals (last) )
- {
- curr = prev;
- prev = curr.getPrevious ();
- }
- }
- do
- {
- if ( locator.contains (curr.getHash ()) || curr.getHeight () < 1 )
- {
- break;
- }
- inventory.add (0, curr.getHash ());
- if ( inventory.size () > limit )
- {
- inventory.remove (limit);
- }
- curr = prev;
- prev = curr.getPrevious ();
- } while ( prev != null );
- return inventory;
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- }
-
- @Override
- public List getLocator ()
- {
- try
- {
- lock.readLock ().lock ();
-
- List locator = new ArrayList ();
- CachedBlock curr = currentHead.getLast ();
- locator.add (curr.getHash ());
- CachedBlock prev = curr.getPrevious ();
- for ( int i = 0, step = 1; prev != null; ++i )
- {
- for ( int j = 0; prev != null && j < step; ++j )
- {
- curr = prev;
- prev = curr.getPrevious ();
- }
- locator.add (curr.getHash ());
- if ( i >= 10 )
- {
- step *= 2;
- }
- }
- return locator;
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- }
-
- @Override
- public long getPeriodLength (String previousHash, int reviewPeriod)
- {
- try
- {
- lock.readLock ().lock ();
-
- CachedBlock cachedPrevious = cachedBlocks.get (previousHash);
-
- return computePeriodLength (cachedPrevious, cachedPrevious.getTime (), reviewPeriod);
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- }
-
- private static class TransactionContext
- {
- Blk block;
- BigInteger blkSumInput = BigInteger.ZERO;
- BigInteger blkSumOutput = BigInteger.ZERO;
- int nsigs = 0;
- boolean coinbase = true;
- TxOutCache resolvedInputs = new ImplementTxOutCache ();
- }
-
- @Override
- public void storeBlock (final Blk b) throws ValidationException
- {
- try
- {
- lock.writeLock ().lock ();
-
- try
- {
- startBatch ();
- lockedStoreBlock (b);
- endBatch ();
- }
- catch ( ValidationException e )
- {
- cancelBatch ();
- throw e;
- }
- catch ( Exception e )
- {
- cancelBatch ();
- throw new ValidationException (e);
- }
- }
- finally
- {
- lock.writeLock ().unlock ();
- }
- }
-
- private void lockedStoreBlock (Blk b) throws ValidationException
- {
- CachedBlock cached = cachedBlocks.get (b.getHash ());
- if ( cached != null )
- {
- return;
- }
- log.trace ("Start storing block " + b.getHash ());
-
- // find previous block
- CachedBlock cachedPrevious = cachedBlocks.get (b.getPreviousHash ());
- if ( cachedPrevious == null )
- {
- throw new ValidationException ("Does not connect to a known block " + b.getHash ());
- }
- Blk prev = null;
- prev = retrieveBlockHeader (cachedPrevious);
-
- if ( b.getCreateTime () > (System.currentTimeMillis () / 1000) * 2 * 60 * 60 || b.getCreateTime () <= getMedianTimePast (cachedPrevious) )
- {
- throw new ValidationException ("Block timestamp out of bounds " + b.getHash ());
- }
-
- boolean checkV2BlockCoinBase = false;
-
- if ( chain.isProduction () )
- {
- if ( enforceV2Block || isSuperMajority (2, cachedPrevious, 950, 1000) )
- {
- if ( !enforceV2Block )
- {
- log.trace ("Majority for V2 blocks reached, enforcing");
- }
- enforceV2Block = true;
- if ( b.getVersion () < 2 )
- {
- throw new ValidationException ("Rejecting version 1 block " + b.getHash ());
- }
- }
- checkV2BlockCoinBase = b.getVersion () >= 2 && isSuperMajority (2, cachedPrevious, 750, 1000);
- }
-
- Head head;
-
- CachedHead cachedPreviousHead = cachedHeads.get (prev.getHeadId ());
- Head previousHead = retrieveHead (cachedPreviousHead);
-
- if ( previousHead.getLeaf ().equals (prev.getHash ()) )
- {
- // continuing
- head = previousHead;
-
- head.setLeaf (b.getHash ());
- head.setHeight (head.getHeight () + 1);
- head.setChainWork (prev.getChainWork () + Difficulty.getDifficulty (b.getDifficultyTarget (), chain));
- head = updateHead (head);
- }
- else
- {
- // branching
- head = new Head ();
-
- head.setPreviousId (prev.getHeadId ());
- head.setPreviousHeight (prev.getHeight ());
- head.setLeaf (b.getHash ());
- head.setHeight (prev.getHeight () + 1);
- head.setChainWork (prev.getChainWork () + Difficulty.getDifficulty (b.getDifficultyTarget (), chain));
- insertHead (head);
- }
- b.setHeadId (head.getId ());
- b.setHeight (head.getHeight ());
- b.setChainWork (head.getChainWork ());
-
- if ( b.getHeight () >= chain.getDifficultyReviewBlocks () && b.getHeight () % chain.getDifficultyReviewBlocks () == 0 )
- {
- long periodLength = computePeriodLength (cachedPrevious, prev.getCreateTime (), chain.getDifficultyReviewBlocks ());
-
- long next = Difficulty.getNextTarget (periodLength, prev.getDifficultyTarget (), chain);
- if ( chain.isProduction () && next != b.getDifficultyTarget () )
- {
- throw new ValidationException ("Difficulty does not match expectation " + b.getHash () + " " + b.toWireDump ());
- }
- }
- else
- {
- if ( chain.isProduction () && b.getDifficultyTarget () != prev.getDifficultyTarget () )
- {
- throw new ValidationException ("Illegal attempt to change difficulty " + b.getHash ());
- }
- }
-
- b.checkHash ();
-
- if ( chain.isProduction () && checkPoints.containsKey (b.getHeight ()) )
- {
- if ( !checkPoints.get (b.getHeight ()).equals (b.getHash ()) )
- {
- throw new ValidationException ("Checkpoint missed");
- }
- }
-
- BigInteger hashAsInteger = new Hash (b.getHash ()).toBigInteger ();
- if ( chain.isProduction () && hashAsInteger.compareTo (Difficulty.getTarget (b.getDifficultyTarget ())) > 0 )
- {
- throw new ValidationException ("Insufficuent proof of work for current difficulty " + b.getHash () + " " + b.toWireDump ());
- }
-
- b.parseTransactions ();
-
- if ( b.getTransactions ().isEmpty () )
- {
- throw new ValidationException ("Block must have transactions " + b.getHash () + " " + b.toWireDump ());
- }
-
- for ( Tx t : b.getTransactions () )
- {
- if ( !isFinal (t, b) )
- {
- throw new ValidationException ("Transactions in a block must be final");
- }
- }
-
- b.checkMerkleRoot ();
-
- ImplementTxOutCacheDelta deltaUTXO = new ImplementTxOutCacheDelta (cachedUTXO);
-
- CachedBlock trunkBlock = cachedPrevious;
- List pathFromTrunkToPrev = new ArrayList ();
- while ( !isOnTrunk (trunkBlock.getHash ()) )
- {
- pathFromTrunkToPrev.add (trunkBlock);
- trunkBlock = trunkBlock.getPrevious ();
- }
- Collections.reverse (pathFromTrunkToPrev);
-
- if ( trunkBlock.getHeight () < (currentHead.getLast ().getHeight () - FORCE_TRUNK) )
- {
- throw new ValidationException ("Attempt to build on or create a branch too far back in history");
- }
-
- if ( currentHead.getLast () != cachedPrevious )
- {
- CachedBlock q = currentHead.getLast ();
- CachedBlock p = q.previous;
- while ( !q.getHash ().equals (trunkBlock.getHash ()) )
- {
- Blk block = retrieveBlock (q);
- backwardCache (block, deltaUTXO, false);
-
- q = p;
- p = q.previous;
- }
-
- for ( CachedBlock block : pathFromTrunkToPrev )
- {
- forwardCache (retrieveBlock (block), deltaUTXO, false);
- }
- }
-
- final TransactionContext tcontext = new TransactionContext ();
- tcontext.block = b;
- tcontext.resolvedInputs = deltaUTXO;
-
- log.trace ("resolving inputs for block " + b.getHash ());
- Set txs = new HashSet ();
- int numberOfOutputs = 0;
- for ( Tx t : b.getTransactions () )
- {
- txs.add (t.getHash ());
- resolveInputs (tcontext.resolvedInputs, b.getHeight (), t);
- for ( TxOut o : t.getOutputs () )
- {
- o.setHeight (b.getHeight ());
- tcontext.resolvedInputs.add (o);
- ++numberOfOutputs;
- }
- }
-
- if ( !chain.isSlave () && (!chain.isProduction () || b.getHeight () > lastCheckPoint || chain.checkBeforeCheckpoint ()) )
- {
- log.trace ("validating block " + b.getHash ());
-
- // BIP30
- if ( currentHead.getLast () != cachedPrevious )
- {
- for ( CachedBlock bl : pathFromTrunkToPrev )
- {
- Blk block = retrieveBlock (bl);
- for ( Tx t : block.getTransactions () )
- {
- if ( txs.contains (t.getHash ()) )
- {
- throw new ValidationException ("BIP30 violation: block contains spent tx " + t.getHash ());
- }
- }
- }
- }
- checkBIP30Compliance (txs, trunkBlock.height);
-
- List> callables = new ArrayList> ();
- for ( final Tx t : b.getTransactions () )
- {
- if ( tcontext.coinbase )
- {
- if ( !isCoinBase (t) )
- {
- throw new ValidationException ("The first transaction of a block must be coinbase");
- }
- try
- {
- if ( checkV2BlockCoinBase )
- {
- ScriptFormat.Reader reader = new ScriptFormat.Reader (t.getInputs ().get (0).getScript ());
- int len = reader.readByte ();
- if ( ScriptFormat.intValue (reader.readBytes (len)) != b.getHeight () )
- {
- throw new ValidationException ("Block height mismatch in coinbase");
- }
- }
- validateTransaction (tcontext, t);
- }
- catch ( TransactionValidationException e )
- {
- throw new ValidationException (e.getMessage () + " " + t.toWireDump (), e);
- }
- tcontext.coinbase = false;
- }
- else
- {
- if ( isCoinBase (t) )
- {
- throw new ValidationException ("Only the first transaction of a block can be coinbase");
- }
- callables.add (new Callable ()
- {
- @Override
- public TransactionValidationException call ()
- {
- try
- {
- validateTransaction (tcontext, t);
- }
- catch ( TransactionValidationException e )
- {
- return e;
- }
- catch ( Exception e )
- {
- return new TransactionValidationException (e, t);
- }
- return null;
- }
- });
- }
- }
- try
- {
- for ( Future e : transactionsProcessor.invokeAll (callables) )
- {
- try
- {
- if ( e.get () != null )
- {
- throw new ValidationException (e.get ().getMessage () + " " + e.get ().getIn () + " " + e.get ().getTx ().toWireDump (), e.get ());
- }
- }
- catch ( ExecutionException e1 )
- {
- throw new ValidationException ("corrupted transaction processor", e1);
- }
- }
- }
- catch ( InterruptedException e1 )
- {
- throw new ValidationException ("interrupted", e1);
- }
- if ( tcontext.nsigs > MAX_BLOCK_SIGOPS )
- {
- throw new ValidationException ("too many signatures in this block ");
- }
- // block reward could actually be less... as in 0000000000004c78956f8643262f3622acf22486b120421f893c0553702ba7b5
- if ( tcontext.blkSumOutput.subtract (tcontext.blkSumInput).longValue () > chain.getRewardForHeight (b.getHeight ()) )
- {
- throw new ValidationException ("Invalid block reward " + b.getHash () + " " + b.toWireDump ());
- }
- }
- // this is last loop before persist since modifying the entities.
-
- BloomFilter filter = BloomFilter.createOptimalFilter (Math.max (5 * numberOfOutputs, 500), 1.0e-10, 0, UpdateMode.none);
-
- for ( Tx t : b.getTransactions () )
- {
- t.setBlock (b);
- for ( TxIn i : t.getInputs () )
- {
- if ( !i.getSourceHash ().equals (Hash.ZERO_HASH_STRING) )
- {
- filter.addOutpoint (i.getSourceHash (), i.getIx ());
- try
- {
- for ( Token token : ScriptFormat.parse (i.getScript ()) )
- {
- if ( token.data != null )
- {
- filter.add (token.data);
- }
- }
- }
- catch ( Exception e )
- {
- // this is best effort
- }
- TxOut source = tcontext.resolvedInputs.get (i.getSourceHash (), i.getIx ());
- if ( source.getId () == null )
- {
- i.setSource (source);
- }
- else
- {
- i.setSource (getSourceReference (source));
- }
-
- }
- }
-
- for ( TxOut o : t.getOutputs () )
- {
- try
- {
- for ( Token token : ScriptFormat.parse (o.getScript ()) )
- {
- if ( token.data != null )
- {
- filter.add (token.data);
- }
- }
- }
- catch ( Exception e )
- {
- // this is best effort
- }
-
- o.setTxHash (t.getHash ());
- o.setHeight (b.getHeight ());
- }
- }
- b.setFilterFunctions ((int) filter.getHashFunctions ());
- b.setFilterMap (filter.getFilter ());
-
- log.trace ("storing block " + b.getHash ());
- insertBlock (b);
-
- // modify transient caches only after persistent changes
- CachedBlock m =
- new CachedBlock (b.getHash (), b.getId (), cachedBlocks.get (b.getPreviousHash ()), b.getCreateTime (), b.getHeight (), (int) b.getVersion (),
- b.getFilterMap (), b.getFilterFunctions ());
- cachedBlocks.put (b.getHash (), m);
-
- CachedHead usingHead = cachedHeads.get (head.getId ());
- if ( usingHead == null )
- {
- cachedHeads.put (head.getId (), usingHead = new CachedHead ());
- usingHead.id = head.getId ();
- usingHead.previous = cachedHeads.get (head.getPreviousId ());
- usingHead.previousHeight = b.getHeight () - 1;
- }
- usingHead.setChainWork (b.getChainWork ());
- usingHead.setHeight (b.getHeight ());
- usingHead.getBlocks ().add (m);
-
- final List removedBlocks = new ArrayList ();
- final List addedBlocks = new ArrayList ();
-
- if ( ByteUtils.isLessThanUnsigned (currentHead.getChainWork (), usingHead.getChainWork ()) )
- {
- // we have a new trunk
- CachedBlock p = currentHead.getLast ();
- CachedBlock q = p.previous;
- while ( !p.equals (trunkBlock) )
- {
- Blk block = retrieveBlock (p);
- backwardCache (block, cachedUTXO, true);
- removedBlocks.add (block);
- p = q;
- q = p.previous;
- }
- List pathToNewHead = new ArrayList ();
- p = m;
- q = p.previous;
- while ( !q.equals (trunkBlock) )
- {
- pathToNewHead.add (q);
- p = q;
- q = p.previous;
- }
- Collections.reverse (pathToNewHead);
-
- for ( CachedBlock cb : pathToNewHead )
- {
- Blk block = retrieveBlock (cb);
- forwardCache (block, cachedUTXO, true);
- addedBlocks.add (block);
- }
-
- forwardCache (b, cachedUTXO, true);
- addedBlocks.add (b);
- currentHead = usingHead;
- }
- else if ( currentHead.getLast () == cachedPrevious )
- {
- forwardCache (b, cachedUTXO, true);
- addedBlocks.add (b);
- currentHead = usingHead;
- }
-
- usingHead.setLast (m);
-
- updateBlockChainCache ();
-
- log.debug ("stored block " + b.getHeight () + " " + b.getHash ());
- if ( !removedBlocks.isEmpty () || !addedBlocks.isEmpty () )
- {
- for ( TrunkListener l : trunkListener )
- {
- l.trunkUpdate (removedBlocks, addedBlocks);
- }
- }
- }
-
- private boolean isFinal (Tx t, Blk b)
- {
- if ( t.getLockTime () == 0 )
- {
- return true;
- }
-
- long nBlockTime = b.getCreateTime ();
- if ( nBlockTime == 0 )
- {
- nBlockTime = System.currentTimeMillis () / 1000;
- }
-
- if ( ByteUtils.isLessThanUnsigned (t.getLockTime (), (ByteUtils.isLessThanUnsigned (t.getLockTime (), 500000000) ? (int) b.getHeight () : nBlockTime)) )
- {
- return true;
- }
-
- for ( TxIn in : t.getInputs () )
- {
- if ( in.getSequence () != 0xFFFFFFFFL )
- {
- return false;
- }
- }
- return true;
- }
-
- private long getMedianTimePast (CachedBlock prev)
- {
- List times = new ArrayList ();
- CachedBlock c = prev;
- CachedBlock p = c.previous;
- int i = 0;
- while ( p != null && i++ < 11 )
- {
- times.add (c.time);
- c = p;
- p = c.previous;
- }
- if ( times.size () == 0 )
- {
- return 0;
- }
-
- Collections.sort (times);
- return times.get (Math.min (5, times.size () - 1));
- }
-
- private long computePeriodLength (CachedBlock cachedPrevious, long previousTime, int reviewPeriod)
- {
- long periodLength = previousTime;
-
- CachedBlock c = null;
- CachedBlock p = cachedPrevious;
- for ( int i = 0; i < reviewPeriod - 1; ++i )
- {
- c = p;
- p = c.getPrevious ();
- }
- periodLength -= p.getTime ();
-
- return periodLength;
- }
-
- private void resolveInputs (TxOutCache resolvedInputs, int blockHeight, Tx t) throws ValidationException
- {
- resolveInputsUsingUTXOCache (resolvedInputs, t);
- resolveInputsUsingDB (resolvedInputs, t);
- checkInputResolution (resolvedInputs, blockHeight, t);
- }
-
- private void resolveInputsUsingUTXOCache (TxOutCache resolvedInputs, Tx t) throws ValidationException
- {
- for ( final TxIn i : t.getInputs () )
- {
- if ( !i.getSourceHash ().equals (Hash.ZERO_HASH_STRING) )
- {
- resolvedInputs.copy (cachedUTXO, i.getSourceHash ());
- }
- }
- }
-
- private void resolveInputsUsingDB (TxOutCache resolvedInputs, Tx t) throws ValidationException
- {
- Map> need = new HashMap> ();
- for ( final TxIn i : t.getInputs () )
- {
- if ( !i.getSourceHash ().equals (Hash.ZERO_HASH_STRING) )
- {
- if ( resolvedInputs.get (i.getSourceHash (), i.getIx ()) == null )
- {
- HashSet ixs = need.get (i.getSourceHash ());
- if ( ixs == null )
- {
- ixs = new HashSet ();
- need.put (i.getSourceHash (), ixs);
- }
- ixs.add (i.getIx ());
- }
- }
- }
- if ( !need.isEmpty () )
- {
- for ( TxOut o : findTxOuts (need) )
- {
- resolvedInputs.add (o);
- }
- }
- }
-
- private void checkInputResolution (TxOutCache resolvedInputs, int height, Tx t) throws ValidationException
- {
- for ( final TxIn i : t.getInputs () )
- {
- if ( !i.getSourceHash ().equals (Hash.ZERO_HASH_STRING) )
- {
- 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 () + "] "
- + t.toWireDump ());
- }
- if ( height != 0 && out.isCoinbase () )
- {
- if ( !chain.allowImmediateCoinbaseSpend () && out.getHeight () > height - COINBASE_MATURITY )
- {
- throw new ValidationException ("coinbase spent too early " + t.toWireDump ());
- }
- }
- }
- }
- }
-
- private boolean checkForRelay (Tx t, TxOutCache resolvedInputs) throws ValidationException
- {
- if ( t.getVersion () != 1 )
- {
- throw new ValidationException ("Transaction version must be 1");
- }
- if ( t.getInputs () == null || t.getInputs ().isEmpty () )
- {
- throw new TransactionValidationException ("a transaction must have inputs", t);
- }
- if ( t.getOutputs () == null || t.getOutputs ().isEmpty () )
- {
- throw new TransactionValidationException ("a transaction must have outputs", t);
- }
-
- long fee = 0;
- for ( TxIn in : t.getInputs () )
- {
- if ( in.getScript ().length > 500 )
- {
- throw new ValidationException ("script length limit exceeded");
- }
- if ( !ScriptFormat.isPushOnly (in.getScript ()) )
- {
- throw new ValidationException ("input script should be push only");
- }
- fee += resolvedInputs.get (in.getSourceHash (), in.getIx ()).getValue ();
- }
- for ( TxOut out : t.getOutputs () )
- {
- if ( !ScriptFormat.isStandard (out.getScript ()) )
- {
- throw new ValidationException ("not a standard output script");
- }
- if ( out.getValue () == 0 )
- {
- throw new ValidationException ("zero output");
- }
- fee -= out.getValue ();
- }
-
- // This node will not relay transactions not paying a minimal fee.
- WireFormat.Writer writer = new WireFormat.Writer ();
- t.toWire (writer);
- return fee >= Math.max (MIN_RELAY_TX_FEE, writer.toByteArray ().length / 1024 * KB_RELAY_TX_FEE);
- }
-
- private boolean isCoinBase (Tx t)
- {
- return t.getInputs ().size () == 1 && t.getInputs ().get (0).getSourceHash ().equals (Hash.ZERO_HASH_STRING);
- }
-
- private void validateTransaction (final TransactionContext tcontext, final Tx t) throws TransactionValidationException
- {
- if ( t.getInputs () == null || t.getInputs ().isEmpty () )
- {
- throw new TransactionValidationException ("a transaction must have inputs", t);
- }
- if ( t.getOutputs () == null || t.getOutputs ().isEmpty () )
- {
- throw new TransactionValidationException ("a transaction must have outputs", t);
- }
-
- if ( isCoinBase (t) )
- {
- if ( tcontext.block == null )
- {
- throw new TransactionValidationException ("coinbase only allowed in a block", t);
- }
- if ( t.getInputs ().get (0).getScript ().length < 2 || t.getInputs ().get (0).getScript ().length > 100 )
- {
- throw new TransactionValidationException ("coinbase script length out of bounds", t);
- }
- }
-
- long sumOut = 0;
- for ( TxOut o : t.getOutputs () )
- {
- if ( o.getValue () < 0 )
- {
- throw new TransactionValidationException ("negative output is not allowed", t);
- }
- if ( o.getValue () > Tx.MAX_MONEY )
- {
- throw new TransactionValidationException ("output too high", t);
- }
- synchronized ( tcontext )
- {
- tcontext.nsigs += ScriptFormat.sigOpCount (o.getScript (), false);
- tcontext.blkSumOutput = tcontext.blkSumOutput.add (BigInteger.valueOf (o.getValue ()));
- }
- sumOut += o.getValue ();
- }
-
- if ( isCoinBase (t) == false )
- {
- long sumIn = 0;
- int inNumber = 0;
- List> callables = new ArrayList> ();
- Map> inputUse = new HashMap> ();
- for ( TxIn i : t.getInputs () )
- {
- HashSet seen = inputUse.get (i.getSourceHash ());
- if ( seen == null )
- {
- inputUse.put (i.getSourceHash (), seen = new HashSet ());
- }
- if ( seen.contains (i.getIx ()) )
- {
- throw new TransactionValidationException ("duplicate input", t);
- }
- seen.add (i.getIx ());
-
- TxOut source = tcontext.resolvedInputs.get (i.getSourceHash (), i.getIx ());
- sumIn += source.getValue ();
- try
- {
- synchronized ( tcontext )
- {
- if ( ScriptFormat.isPayToScriptHash (source.getScript ()) )
- {
- ScriptFormat.Tokenizer tokenizer = new ScriptFormat.Tokenizer (i.getScript ());
- byte[] last = null;
- while ( tokenizer.hashMoreElements () )
- {
- last = tokenizer.nextToken ().data;
- }
- tcontext.nsigs += ScriptFormat.sigOpCount (last, true);
- }
- else
- {
- tcontext.nsigs += ScriptFormat.sigOpCount (i.getScript (), false);
- }
- tcontext.blkSumInput = tcontext.blkSumInput.add (BigInteger.valueOf (source.getValue ()));
- }
- }
- catch ( ValidationException e )
- {
- throw new TransactionValidationException (e, t);
- }
-
- final ScriptEvaluation evaluation = new ScriptEvaluation (t, inNumber++, source);
- callables.add (new Callable ()
- {
- @Override
- public TransactionValidationException call () throws Exception
- {
- try
- {
- if ( !evaluation.evaluate (chain.isProduction ()) )
- {
- return new TransactionValidationException ("The transaction script does not evaluate to true in input", t, evaluation.getInr ());
- }
- }
- catch ( Exception e )
- {
- return new TransactionValidationException (e, t, evaluation.getInr ());
- }
- return null;
- }
- });
- }
- if ( sumOut > sumIn )
- {
- throw new TransactionValidationException ("Transaction value out more than in", t);
- }
- List> results;
- try
- {
- results = inputProcessor.invokeAll (callables);
- }
- catch ( InterruptedException e1 )
- {
- throw new TransactionValidationException (e1, t);
- }
- for ( Future r : results )
- {
- TransactionValidationException ex;
- try
- {
- ex = r.get ();
- }
- catch ( InterruptedException e )
- {
- throw new TransactionValidationException (e, t);
- }
- catch ( ExecutionException e )
- {
- throw new TransactionValidationException (e, t);
- }
- if ( ex != null )
- {
- throw ex;
- }
- }
- }
- }
-
- @Override
- public String getHeadHash ()
- {
- try
- {
- lock.readLock ().lock ();
-
- return currentHead.getLast ().getHash ();
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- }
-
- @Override
- public void resetStore (Chain chain) throws ValidationException
- {
- log.info ("Reset block store");
- clearStore ();
-
- this.chain = chain;
-
- Blk genesis = chain.getGenesis ();
- Head h = new Head ();
- h.setLeaf (genesis.getHash ());
- h.setHeight (0);
- h.setChainWork (Difficulty.getDifficulty (genesis.getDifficultyTarget (), chain));
- insertHead (h);
- genesis.setHeadId (h.getId ());
- insertBlock (genesis);
- }
-
- @Override
- public Blk getBlock (String hash) throws ValidationException
- {
-
- CachedBlock cached = null;
- try
- {
- lock.readLock ().lock ();
- cached = cachedBlocks.get (hash);
- if ( cached == null )
- {
- return null;
- }
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- return retrieveBlock (cached);
- }
-
- @Override
- public Blk getBlockHeader (String hash) throws ValidationException
- {
-
- CachedBlock cached = null;
- try
- {
- lock.readLock ().lock ();
- cached = cachedBlocks.get (hash);
- if ( cached == null )
- {
- return null;
- }
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- return retrieveBlockHeader (cached);
- }
-
- @Override
- public void resolveTransactionInputs (Tx t, TxOutCache resolvedInputs) throws ValidationException
- {
- try
- {
- lock.readLock ().lock ();
-
- resolveInputs (resolvedInputs, 0, t);
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- }
-
- @Override
- public boolean validateTransaction (Tx t, TxOutCache resolvedInputs) throws ValidationException
- {
- try
- {
- lock.readLock ().lock ();
-
- if ( t.getInputs () == null )
- {
- throw new ValidationException ("a transaction must have inputs");
- }
-
- resolveInputs (resolvedInputs, 0, t);
-
- TransactionContext tcontext = new TransactionContext ();
- tcontext.block = null;
- tcontext.coinbase = false;
- tcontext.nsigs = 0;
- tcontext.resolvedInputs = resolvedInputs;
-
- boolean relay = !chain.isProduction () || checkForRelay (t, resolvedInputs);
-
- validateTransaction (tcontext, t);
-
- return relay;
- }
- finally
- {
- lock.readLock ().unlock ();
- }
- }
-}
diff --git a/server/src/main/java/com/bitsofproof/supernode/core/Chain.java b/server/src/main/java/com/bitsofproof/supernode/core/Chain.java
deleted file mode 100644
index deb67058..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/Chain.java
+++ /dev/null
@@ -1,51 +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.math.BigInteger;
-
-import com.bitsofproof.supernode.model.Blk;
-
-public interface Chain
-{
- public Blk getGenesis ();
-
- public long getMagic ();
-
- public int getPort ();
-
- public byte[] getAlertKey ();
-
- public boolean checkBeforeCheckpoint ();
-
- public boolean allowImmediateCoinbaseSpend ();
-
- public BigInteger getMinimumTarget ();
-
- public int getDifficultyReviewBlocks ();
-
- public int getTargetBlockTime ();
-
- public long getRewardForHeight (int height);
-
- public boolean isProduction ();
-
- 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/ChainLoader.java b/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java
deleted file mode 100644
index f0d049b4..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/ChainLoader.java
+++ /dev/null
@@ -1,371 +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.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;
-
-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;
-import com.bitsofproof.supernode.messages.GetDataMessage;
-import com.bitsofproof.supernode.messages.InvMessage;
-import com.bitsofproof.supernode.model.Blk;
-
-public class ChainLoader
-{
- private static final Logger log = LoggerFactory.getLogger (ChainLoader.class);
-
- private int timeout = 60;
-
- private final Map> knownInventory = new HashMap> ();
- private final Map> requests = new HashMap> ();
- private final Map invTimer = new HashMap ();
- private final Map> pendingOn = Collections.synchronizedMap (new HashMap> ());
- private final Map havePending = Collections.synchronizedMap (new HashMap ());
-
- private final BlockStore store;
- private final BitcoinNetwork network;
-
- private final static int ORPHANLIMIT = 1000;
-
- private static class KnownBlock
- {
- private int nr;
- private String hash;
- }
-
- public ChainLoader (final BitcoinNetwork network)
- {
- this.network = network;
- store = network.getStore ();
-
- network.addListener ("block", new BitcoinMessageListener ()
- {
- @Override
- public void process (BlockMessage m, final BitcoinPeer peer)
- {
- boolean validatedBlock = false;
-
- Blk block = m.getBlock ();
- log.debug ("received block " + block.getHash () + " from " + peer.getAddress ());
- if ( store.isStoredBlock (block.getPreviousHash ()) )
- {
- try
- {
- havePending.remove (block.getHash ());
- store.storeBlock (block);
- validatedBlock = true;
-
- if ( pendingOn.containsKey (block.getHash ()) )
- {
- storePending (pendingOn.get (block.getHash ()));
- pendingOn.remove (block.getHash ());
- }
- }
- catch ( ValidationException e )
- {
- log.debug ("REJECTING block " + block.getHash () + " from " + peer.getAddress ());
- }
- }
- else
- {
- GetDataMessage getparent = (GetDataMessage) peer.createMessage ("getdata");
- getparent.getBlocks ().add (new Hash (block.getPreviousHash ()).toByteArray ());
- peer.send (getparent);
-
- if ( !havePending.containsKey (block.getHash ()) )
- {
- ArrayList pendingList = pendingOn.get (block.getPreviousHash ());
- if ( pendingList == null )
- {
- pendingList = new ArrayList ();
- pendingOn.put (block.getPreviousHash (), pendingList);
- }
- pendingList.add (block);
-
- havePending.put (block.getHash (), block);
- if ( havePending.size () > ORPHANLIMIT )
- {
- havePending.clear ();
- pendingOn.clear ();
- log.warn ("Orphan block limit exceeded. Resetting cache.");
- }
- }
- }
-
- HashSet peerRequests;
- synchronized ( knownInventory )
- {
- peerRequests = requests.get (peer);
- if ( peerRequests == null )
- {
- return;
- }
- if ( peerRequests.size () == 1 )
- {
- if ( validatedBlock )
- {
- sendBlock (block, peer);
- }
- }
- peerRequests.remove (block.getHash ());
- }
- if ( peerRequests.isEmpty () )
- {
- if ( peer.getHeight () > store.getChainHeight () )
- {
- getBlockInventory (network, store, peer);
- }
- }
- }
-
- private void storePending (List blocks) throws ValidationException
- {
- Iterator i = blocks.iterator ();
- while ( i.hasNext () )
- {
- Blk b = i.next ();
- havePending.remove (b.getHash ());
- store.storeBlock (b);
- List next = pendingOn.get (b.getHash ());
- if ( next != null )
- {
- if ( next.size () == 1 )
- {
- // avoid deep recursion for simple chain
- i = next.iterator ();
- }
- else
- {
- storePending (next);
- }
- pendingOn.remove (b.getHash ());
- }
- }
- }
- });
-
- network.addListener ("inv", new BitcoinMessageListener ()
- {
- @Override
- public void process (InvMessage m, BitcoinPeer peer)
- {
- if ( !m.getBlockHashes ().isEmpty () )
- {
- log.trace ("received inventory of " + m.getBlockHashes ().size () + " blocks from " + peer.getAddress ());
- synchronized ( knownInventory )
- {
- Set k = knownInventory.get (peer);
- if ( k == null )
- {
- return; // disconnected peer
- }
- if ( m.getBlockHashes ().size () > 1 )
- {
- Long job = invTimer.get (peer);
- if ( job != null )
- {
- network.cancelJob (job);
- invTimer.remove (peer);
- }
- }
- int n = k.size ();
- for ( byte[] h : m.getBlockHashes () )
- {
- String hash = new Hash (h).toString ();
- if ( !havePending.containsKey (hash) && !store.isStoredBlock (hash) )
- {
- KnownBlock kn = new KnownBlock ();
- kn.nr = n++;
- kn.hash = hash;
- k.add (kn);
- }
- }
- }
- getBlocks (network, peer);
- }
- }
- });
-
- network.addPeerListener (new BitcoinPeerListener ()
- {
-
- @Override
- public void remove (final BitcoinPeer peer)
- {
- boolean nowork = false;
- synchronized ( knownInventory )
- {
- knownInventory.remove (peer);
- requests.remove (peer);
- invTimer.remove (peer);
- nowork = invTimer.isEmpty ();
- }
- if ( nowork )
- {
- for ( BitcoinPeer p : network.getConnectPeers () )
- {
- if ( store.getChainHeight () < p.getHeight () )
- {
- getBlockInventory (network, store, p);
- }
- }
- }
- }
-
- @Override
- public void add (BitcoinPeer peer)
- {
- synchronized ( knownInventory )
- {
- requests.put (peer, new HashSet ());
- knownInventory.put (peer, new TreeSet (incomingOrder));
- }
- if ( peer.getHeight () > store.getChainHeight () )
- {
- getBlockInventory (network, store, peer);
- }
- }
- });
- }
-
- private void sendBlock (Blk b, BitcoinPeer peer)
- {
- for ( BitcoinPeer p : network.getConnectPeers () )
- {
- if ( p != peer )
- {
- InvMessage bm = (InvMessage) p.createMessage ("inv");
- bm.getBlockHashes ().add (new Hash (b.getHash ()).toByteArray ());
- p.send (bm);
- }
- }
- log.trace ("sent block " + b.getHash ());
- }
-
- public int getTimeout ()
- {
- return timeout;
- }
-
- public void setTimeout (int inventoryTimeout)
- {
- this.timeout = inventoryTimeout;
- }
-
- private void getBlocks (final BitcoinNetwork network, final BitcoinPeer peer)
- {
- GetDataMessage gdm = (GetDataMessage) peer.createMessage ("getdata");
- synchronized ( knownInventory )
- {
- if ( !knownInventory.containsKey (peer) )
- {
- return;
- }
- for ( KnownBlock b : knownInventory.get (peer) )
- {
- boolean askedElswhere = false;
- for ( Set ps : requests.values () )
- {
- if ( ps.contains (b.hash) )
- {
- askedElswhere = true;
- break;
- }
- }
- if ( !askedElswhere )
- {
- gdm.getBlocks ().add (new Hash (b.hash).toByteArray ());
- requests.get (peer).add (b.hash);
- }
- }
- knownInventory.get (peer).clear ();
- }
- if ( gdm.getBlocks ().size () > 0 )
- {
- peer.send (gdm);
- log.trace ("asking for " + gdm.getBlocks ().size () + " blocks from " + peer.getAddress ());
- }
- }
-
- private void getBlockInventory (final BitcoinNetwork network, final BlockStore store, final BitcoinPeer peer)
- {
- synchronized ( knownInventory )
- {
- if ( invTimer.containsKey (peer) )
- {
- return;
- }
- HashSet peerRequests = requests.get (peer);
- if ( peerRequests != null && !peerRequests.isEmpty () )
- {
- return;
- }
- }
- GetBlocksMessage gbm = (GetBlocksMessage) peer.createMessage ("getblocks");
- for ( String h : store.getLocator () )
- {
- gbm.getHashes ().add (new Hash (h).toByteArray ());
- }
- gbm.setLastHash (Hash.ZERO_HASH.toByteArray ());
- if ( !gbm.getHashes ().isEmpty () )
- {
- log.trace ("Sending inventory request to " + peer.getAddress ());
- peer.send (gbm);
- invTimer.put (peer, network.scheduleJob (new Runnable ()
- {
- @Override
- public void run ()
- {
- log.trace ("Peer did not answer to inventory requests " + peer.getAddress ());
- peer.disconnect ();
- }
- }, timeout, TimeUnit.SECONDS));
- }
- }
-
- private static final Comparator incomingOrder = new Comparator ()
- {
- @Override
- public int compare (KnownBlock arg0, KnownBlock arg1)
- {
- int diff = arg0.nr - arg1.nr;
- if ( diff != 0 )
- {
- return diff;
- }
- else
- {
- return arg0.equals (arg1) ? 0 : arg0.hashCode () - arg1.hashCode ();
- }
- }
- };
-}
diff --git a/server/src/main/java/com/bitsofproof/supernode/core/DNSDiscovery.java b/server/src/main/java/com/bitsofproof/supernode/core/DNSDiscovery.java
deleted file mode 100644
index 2f1a8a1d..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/DNSDiscovery.java
+++ /dev/null
@@ -1,75 +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.net.InetSocketAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-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 ()
- {
- log.trace ("Discovering network using DNS seed");
- int n = 0;
- List al = new ArrayList ();
- for ( String hostName : seedHosts )
- {
- log.trace ("Obtain addresses from " + hostName);
- InetAddress[] hostAddresses;
- try
- {
- hostAddresses = InetAddress.getAllByName (hostName);
- for ( InetAddress inetAddress : hostAddresses )
- {
- al.add (new InetSocketAddress (inetAddress, chain.getPort ()));
- ++n;
- }
- }
- catch ( UnknownHostException e )
- {
- }
- }
- Collections.shuffle (al);
- log.trace ("Found " + n + " addresses of seed hosts");
- return al;
- }
-
- public String[] getSeedHosts ()
- {
- return seedHosts;
- }
-
- public void setSeedHosts (String[] seedHosts)
- {
- this.seedHosts = seedHosts;
- }
-
-}
diff --git a/server/src/main/java/com/bitsofproof/supernode/core/Difficulty.java b/server/src/main/java/com/bitsofproof/supernode/core/Difficulty.java
deleted file mode 100644
index 34348965..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/Difficulty.java
+++ /dev/null
@@ -1,53 +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.math.BigInteger;
-
-public class Difficulty
-{
- public static BigInteger getTarget (long compactTarget)
- {
- return BigInteger.valueOf (compactTarget & 0x7fffffL).shiftLeft ((int) (8 * ((compactTarget >>> 24) - 3)));
- }
-
- public static long getDifficulty (long compactTarget, Chain chain)
- {
- return chain.getMinimumTarget ().divide (getTarget (compactTarget)).longValue ();
- }
-
- public static long getCompactTarget (BigInteger target)
- {
- int log2 = target.bitLength ();
- int s = (log2 / 8 + 1) * 8;
-
- return (target.shiftRight (s - 24)).or (BigInteger.valueOf ((s - 24) / 8 + 3).shiftLeft (24)).longValue ();
- }
-
- public static long getNextTarget (long periodLength, long currentTarget, Chain chain)
- {
- // Limit the adjustment step.
- periodLength = Math.max (Math.min (periodLength, chain.getTargetBlockTime () * 4), chain.getTargetBlockTime () / 4);
- BigInteger newTarget =
- (getTarget (currentTarget).multiply (BigInteger.valueOf (periodLength))).divide (BigInteger.valueOf (chain.getTargetBlockTime ()));
- // not simpler than this
- if ( newTarget.compareTo (chain.getMinimumTarget ()) > 0 )
- {
- newTarget = chain.getMinimumTarget ();
- }
- return getCompactTarget (newTarget);
- }
-}
diff --git a/server/src/main/java/com/bitsofproof/supernode/core/Discovery.java b/server/src/main/java/com/bitsofproof/supernode/core/Discovery.java
deleted file mode 100644
index 1ba25793..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/Discovery.java
+++ /dev/null
@@ -1,24 +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.InetSocketAddress;
-import java.util.List;
-
-public interface Discovery
-{
- 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
deleted file mode 100644
index 5d162a8b..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/DiscoveryChain.java
+++ /dev/null
@@ -1,44 +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.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.List;
-
-public class DiscoveryChain implements Discovery
-{
- private final List discoveryChain;
-
- public DiscoveryChain (List chain)
- {
- discoveryChain = chain;
- }
-
- @Override
- public List discover ()
- {
- for ( Discovery discovery : discoveryChain )
- {
- List addresses = discovery.discover ();
- if ( !addresses.isEmpty () )
- {
- return addresses;
- }
- }
- 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
deleted file mode 100644
index 4063ce4f..00000000
--- a/server/src/main/java/com/bitsofproof/supernode/core/FixedAddressDiscovery.java
+++ /dev/null
@@ -1,69 +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.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
-{
- private static final Logger log = LoggerFactory.getLogger (FixedAddressDiscovery.class);
-
- private String connectTo;
-
- @Autowired
- private Chain chain;
-
- @Override
- public List