mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-04-19 07:15:54 -04:00
Use encryption/sig for all messages
This commit is contained in:
parent
32c56dad1a
commit
0ec86550b0
@ -22,6 +22,7 @@ import io.bitsquare.btc.BitcoinNetwork;
|
||||
import io.bitsquare.btc.RegTestHost;
|
||||
import io.bitsquare.btc.UserAgent;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.crypto.KeyStorage;
|
||||
import io.bitsquare.p2p.BootstrapNodes;
|
||||
import io.bitsquare.p2p.tomp2p.TomP2PModule;
|
||||
import io.bitsquare.storage.Storage;
|
||||
@ -166,6 +167,7 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
||||
setProperty(WalletService.PREFIX_KEY, appName);
|
||||
|
||||
setProperty(Storage.DIR_KEY, Paths.get(appDataDir, "db").toString());
|
||||
setProperty(KeyStorage.DIR_KEY, Paths.get(appDataDir, "keys").toString());
|
||||
|
||||
setProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY, bootstrapNodePort);
|
||||
}
|
||||
|
@ -21,9 +21,9 @@ import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.arbitration.ArbitratorService;
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.crypto.KeyRing;
|
||||
import io.bitsquare.p2p.tomp2p.TomP2PDHTService;
|
||||
import io.bitsquare.p2p.tomp2p.TomP2PNode;
|
||||
import io.bitsquare.user.User;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@ -53,8 +53,8 @@ public class TomP2PArbitratorService extends TomP2PDHTService implements Arbitra
|
||||
private final CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
@Inject
|
||||
public TomP2PArbitratorService(TomP2PNode tomP2PNode, User user) {
|
||||
super(tomP2PNode, user);
|
||||
public TomP2PArbitratorService(TomP2PNode tomP2PNode, KeyRing keyRing) {
|
||||
super(tomP2PNode, keyRing);
|
||||
}
|
||||
|
||||
public void addArbitrator(Arbitrator arbitrator, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
|
@ -20,7 +20,7 @@ package io.bitsquare.btc;
|
||||
import io.bitsquare.btc.listeners.AddressConfidenceListener;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.btc.listeners.TxConfidenceListener;
|
||||
import io.bitsquare.crypto.SignatureService;
|
||||
import io.bitsquare.crypto.CryptoService;
|
||||
|
||||
import org.bitcoinj.core.AbstractWalletEventListener;
|
||||
import org.bitcoinj.core.Address;
|
||||
@ -95,7 +95,7 @@ public class WalletService {
|
||||
private final TradeWalletService tradeWalletService;
|
||||
private final AddressEntryList addressEntryList;
|
||||
private final NetworkParameters params;
|
||||
private final SignatureService signatureService;
|
||||
private final CryptoService cryptoService;
|
||||
private final File walletDir;
|
||||
private final String walletPrefix;
|
||||
private final UserAgent userAgent;
|
||||
@ -111,14 +111,14 @@ public class WalletService {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public WalletService(BitcoinNetwork bitcoinNetwork, RegTestHost regTestHost, SignatureService signatureService,
|
||||
public WalletService(BitcoinNetwork bitcoinNetwork, RegTestHost regTestHost, CryptoService cryptoService,
|
||||
TradeWalletService tradeWalletService, AddressEntryList addressEntryList, UserAgent userAgent,
|
||||
@Named(DIR_KEY) File walletDir, @Named(PREFIX_KEY) String walletPrefix) {
|
||||
this.regTestHost = regTestHost;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
this.addressEntryList = addressEntryList;
|
||||
this.params = bitcoinNetwork.getParameters();
|
||||
this.signatureService = signatureService;
|
||||
this.cryptoService = cryptoService;
|
||||
this.walletDir = walletDir;
|
||||
this.walletPrefix = walletPrefix;
|
||||
this.userAgent = userAgent;
|
||||
@ -208,7 +208,8 @@ public class WalletService {
|
||||
}, Threading.USER_THREAD);
|
||||
walletAppKit.startAsync();
|
||||
|
||||
return status.mergeWith(downloadProgress).timeout(30, TimeUnit.SECONDS);
|
||||
return status.timeout(30, TimeUnit.SECONDS);
|
||||
//return status.mergeWith(downloadProgress).timeout(30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void initWallet() {
|
||||
@ -457,7 +458,7 @@ public class WalletService {
|
||||
|
||||
Transaction tx = new Transaction(params);
|
||||
|
||||
byte[] data = signatureService.digestMessageWithSignature(getRegistrationAddressEntry().getKeyPair(), stringifiedFiatAccounts);
|
||||
byte[] data = cryptoService.digestMessageWithSignature(getRegistrationAddressEntry().getKeyPair(), stringifiedFiatAccounts);
|
||||
tx.addOutput(Transaction.MIN_NONDUST_OUTPUT, new ScriptBuilder().op(OP_RETURN).data(data).build());
|
||||
|
||||
// We don't take a fee at the moment
|
||||
|
@ -31,8 +31,6 @@ public class CryptoModule extends BitsquareModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SignatureService.class).in(Singleton.class);
|
||||
bind(HashService.class).in(Singleton.class);
|
||||
bind(EncryptionService.class).in(Singleton.class);
|
||||
bind(CryptoService.class).in(Singleton.class);
|
||||
}
|
||||
}
|
||||
|
217
core/src/main/java/io/bitsquare/crypto/CryptoService.java
Normal file
217
core/src/main/java/io/bitsquare/crypto/CryptoService.java
Normal file
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.crypto;
|
||||
|
||||
import io.bitsquare.p2p.Message;
|
||||
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Utils;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.security.SignedObject;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.spongycastle.util.encoders.Base64;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SealedObject;
|
||||
import javax.crypto.SecretKey;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
public class CryptoService<T> {
|
||||
private static final Logger log = LoggerFactory.getLogger(CryptoService.class);
|
||||
public static final String DHT_SIGN_KEY_ALGO = "DSA";
|
||||
public static final String MSG_SIGN_KEY_ALGO = "DSA";
|
||||
public static final String MSG_ENCR_KEY_ALGO = "RSA";
|
||||
|
||||
private static final String SYM_ENCR_KEY_ALGO = "AES";
|
||||
private static final String SYM_CIPHER = "AES";
|
||||
private static final String ASYM_CIPHER = "RSA"; //RSA/ECB/PKCS1Padding
|
||||
private static final String MSG_SIGN_ALGO = "SHA1withDSA";
|
||||
|
||||
public static KeyPair generateDhtSignatureKeyPair() throws NoSuchAlgorithmException {
|
||||
long ts = System.currentTimeMillis();
|
||||
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(DHT_SIGN_KEY_ALGO);
|
||||
keyPairGenerator.initialize(1024);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
log.debug("Generate dhtSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
public static KeyPair generateMsgSignatureKeyPair() throws NoSuchAlgorithmException {
|
||||
long ts = System.currentTimeMillis();
|
||||
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(MSG_SIGN_KEY_ALGO);
|
||||
keyPairGenerator.initialize(1024);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
log.debug("Generate dhtSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
public static KeyPair generateMsgEncryptionKeyPair() throws NoSuchAlgorithmException {
|
||||
long ts = System.currentTimeMillis();
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(MSG_ENCR_KEY_ALGO);
|
||||
keyPairGenerator.initialize(2048);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
log.debug("Generate msgEncryptionKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
private KeyRing keyRing;
|
||||
|
||||
@Inject
|
||||
public CryptoService(KeyRing keyRing) {
|
||||
this.keyRing = keyRing;
|
||||
}
|
||||
|
||||
public SealedAndSignedMessage encryptAndSignMessage(PubKeyRing pubKeyRing, Message message) throws CryptoException {
|
||||
long ts = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
// Create symmetric key
|
||||
KeyGenerator keyGenerator = KeyGenerator.getInstance(SYM_ENCR_KEY_ALGO);
|
||||
keyGenerator.init(128);
|
||||
SecretKey secretKey = keyGenerator.generateKey();
|
||||
|
||||
// Encrypt secretKey with peers pubKey using SealedObject
|
||||
Cipher cipherAsym = Cipher.getInstance(ASYM_CIPHER);
|
||||
cipherAsym.init(Cipher.ENCRYPT_MODE, pubKeyRing.getMsgEncryptionPubKey());
|
||||
SealedObject sealedSecretKey = new SealedObject(secretKey, cipherAsym);
|
||||
|
||||
// Sign (hash of) message and pack it into SignedObject
|
||||
SignedObject signedMessage = new SignedObject(message, keyRing.getMsgSignatureKeyPair().getPrivate(), Signature.getInstance(MSG_SIGN_ALGO));
|
||||
|
||||
// // Encrypt signedMessage with secretKey using SealedObject
|
||||
Cipher cipherSym = Cipher.getInstance(SYM_CIPHER);
|
||||
cipherSym.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||
SealedObject sealedMessage = new SealedObject(signedMessage, cipherSym);
|
||||
|
||||
SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(sealedSecretKey,
|
||||
sealedMessage,
|
||||
keyRing.getMsgSignatureKeyPair().getPublic()
|
||||
);
|
||||
log.debug("Encryption needed {} ms", System.currentTimeMillis() - ts);
|
||||
return sealedAndSignedMessage;
|
||||
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException
|
||||
| IllegalBlockSizeException | IOException | SignatureException e) {
|
||||
throw new CryptoException(e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public MessageWithPubKey decryptAndVerifyMessage(SealedAndSignedMessage sealedAndSignedMessage) throws CryptoException {
|
||||
long ts = System.currentTimeMillis();
|
||||
try {
|
||||
SealedObject sealedSecretKey = sealedAndSignedMessage.getSealedSecretKey();
|
||||
SealedObject sealedMessage = sealedAndSignedMessage.getSealedMessage();
|
||||
PublicKey signaturePubKey = sealedAndSignedMessage.getSignaturePubKey();
|
||||
|
||||
// Decrypt secretKey with my privKey
|
||||
Cipher cipherAsym = Cipher.getInstance(ASYM_CIPHER);
|
||||
cipherAsym.init(Cipher.DECRYPT_MODE, keyRing.getMsgEncryptionKeyPair().getPrivate());
|
||||
Object secretKeyObject = sealedSecretKey.getObject(cipherAsym);
|
||||
assert secretKeyObject instanceof SecretKey;
|
||||
SecretKey secretKey = (SecretKey) secretKeyObject;
|
||||
|
||||
// Decrypt signedMessage with secretKey
|
||||
Cipher cipherSym = Cipher.getInstance(SYM_CIPHER);
|
||||
cipherSym.init(Cipher.DECRYPT_MODE, secretKey);
|
||||
Object signedMessageObject = sealedMessage.getObject(cipherSym);
|
||||
assert signedMessageObject instanceof SignedObject;
|
||||
SignedObject signedMessage = (SignedObject) signedMessageObject;
|
||||
|
||||
// Verify message with peers pubKey
|
||||
if (signedMessage.verify(signaturePubKey, Signature.getInstance(MSG_SIGN_ALGO))) {
|
||||
// Get message
|
||||
Object messageObject = signedMessage.getObject();
|
||||
assert messageObject instanceof Message;
|
||||
log.debug("Decryption needed {} ms", System.currentTimeMillis() - ts);
|
||||
return new MessageWithPubKey((Message) messageObject, signaturePubKey);
|
||||
}
|
||||
else {
|
||||
throw new CryptoException("Signature is not valid");
|
||||
}
|
||||
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException |
|
||||
ClassNotFoundException | IllegalBlockSizeException | IOException | SignatureException e) {
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String signMessage(ECKey key, Sha256Hash hash) {
|
||||
ECKey.ECDSASignature sig = key.sign(hash, null);
|
||||
// Now we have to work backwards to figure out the recId needed to recover the signature.
|
||||
int recId = -1;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ECKey k = ECKey.recoverFromSignature(i, sig, hash, key.isCompressed());
|
||||
if (k != null && k.getPubKeyPoint().equals(key.getPubKeyPoint())) {
|
||||
recId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (recId == -1)
|
||||
throw new RuntimeException("Could not construct a recoverable key. This should never happen.");
|
||||
int headerByte = recId + 27 + (key.isCompressed() ? 4 : 0);
|
||||
byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 bytes for S
|
||||
sigData[0] = (byte) headerByte;
|
||||
System.arraycopy(Utils.bigIntegerToBytes(sig.r, 32), 0, sigData, 1, 32);
|
||||
System.arraycopy(Utils.bigIntegerToBytes(sig.s, 32), 0, sigData, 33, 32);
|
||||
return new String(Base64.encode(sigData), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
|
||||
public byte[] digestMessageWithSignature(ECKey key, String message) {
|
||||
String signedMessage = signMessage(key, message);
|
||||
return Utils.sha256hash160(message.concat(signedMessage).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
public String signMessage(ECKey key, String message) {
|
||||
byte[] data = Utils.formatMessageForSigning(message);
|
||||
Sha256Hash hash = Sha256Hash.hashTwice(data);
|
||||
return signMessage(key, hash);
|
||||
}
|
||||
|
||||
public Sha256Hash hash(String message) {
|
||||
byte[] data = Utils.formatMessageForSigning(message);
|
||||
return Sha256Hash.hashTwice(data);
|
||||
}
|
||||
}
|
||||
|
@ -1,240 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.crypto;
|
||||
|
||||
import io.bitsquare.app.Version;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.Utils;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Security;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
public class EncryptionService<T> {
|
||||
private static final Logger log = LoggerFactory.getLogger(EncryptionService.class);
|
||||
private static final String ALGO_SYM = "AES";
|
||||
private static final String CIPHER_SYM = "AES";// AES/CBC/PKCS5Padding
|
||||
private static final String ALGO_ASYM = "RSA";
|
||||
private static final String CIPHER_ASYM = "RSA/ECB/PKCS1Padding";
|
||||
|
||||
private static final int MAX_SIZE = 10000; // in bytes
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
private SignatureService signatureService;
|
||||
|
||||
@Inject
|
||||
public EncryptionService(SignatureService signatureService) {
|
||||
this.signatureService = signatureService;
|
||||
}
|
||||
|
||||
public KeyPair getGeneratedDSAKeyPair() throws NoSuchAlgorithmException {
|
||||
long ts = System.currentTimeMillis();
|
||||
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
|
||||
keyPairGenerator.initialize(1024);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
log.debug("getGeneratedDSAKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
public KeyPair getGeneratedRSAKeyPair() throws NoSuchAlgorithmException {
|
||||
long ts = System.currentTimeMillis();
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGO_ASYM);
|
||||
keyPairGenerator.initialize(1024);
|
||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||
log.debug("getGeneratedRSAKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
public byte[] encryptObject(PublicKey publicKey, ECKey signatureKeyPair, Object object) throws CryptoException {
|
||||
return encryptBytes(publicKey, signatureKeyPair, Utilities.objectToBytArray(object));
|
||||
}
|
||||
|
||||
public byte[] encryptMessage(PublicKey publicKey, ECKey signatureKeyPair, Message object) throws CryptoException {
|
||||
return encryptBytes(publicKey, signatureKeyPair, Utilities.objectToBytArray(object));
|
||||
}
|
||||
|
||||
public byte[] encryptBytes(PublicKey publicKey, ECKey signatureKeyPair, byte[] plainText) throws CryptoException {
|
||||
long ts = System.currentTimeMillis();
|
||||
|
||||
if (plainText.length == 0)
|
||||
throw new CryptoException("Input data is null.");
|
||||
|
||||
try {
|
||||
// Create symmetric key
|
||||
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGO_SYM);
|
||||
keyGenerator.init(128);
|
||||
SecretKey oneTimeKey = keyGenerator.generateKey();
|
||||
|
||||
// Encrypt secretKey with asymmetric key (16 bytes)
|
||||
Cipher cipherAsym = Cipher.getInstance(CIPHER_ASYM);
|
||||
cipherAsym.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
|
||||
byte[] encryptedOneTimeKey = cipherAsym.doFinal(oneTimeKey.getEncoded());
|
||||
|
||||
// Create signature of plainText (65 bytes)
|
||||
ECKey.ECDSASignature signature = signatureService.signBytes(signatureKeyPair, plainText).toCanonicalised();
|
||||
byte[] sig = signature.encodeToDER(); // has 70-72 bytes ;-(
|
||||
byte[] sigLength = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(sig.length).array();
|
||||
|
||||
// Encrypt plainText with symmetric key
|
||||
Cipher cipherSym = Cipher.getInstance(CIPHER_SYM);
|
||||
cipherSym.init(Cipher.ENCRYPT_MODE, oneTimeKey);
|
||||
byte[] cipherText = cipherSym.doFinal(plainText);
|
||||
|
||||
// payload
|
||||
byte[] payload = Utilities.concatByteArrays(sigLength, sig, signatureKeyPair.getPubKey(), encryptedOneTimeKey, cipherText);
|
||||
byte[] payloadLength = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(payload.length).array();
|
||||
|
||||
// Checksum
|
||||
byte[] checksum = Utils.sha256hash160(Utilities.concatByteArrays(payloadLength, payload));
|
||||
|
||||
// 1 byte version | 20 byte checksum | 4 byte payload length | n bytes payload
|
||||
// payload consist of: 4 byte sigLength | sigLength(70-72) bytes for sig | 33 bytes for signaturePubKey | 128 bytes for encryptedOneTimeKey |
|
||||
// remaining
|
||||
// bytes for cipherText
|
||||
byte[] result = Utilities.concatByteArrays(Version.NETWORK_PROTOCOL_VERSION, checksum, payloadLength, payload);
|
||||
log.debug("result.length " + result.length);
|
||||
log.debug("Encryption needed {} ms", System.currentTimeMillis() - ts);
|
||||
return result;
|
||||
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public T decryptToMessage(PrivateKey privateKey, byte[] data) throws IllegalBlockSizeException, InvalidKeyException,
|
||||
BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, CryptoException {
|
||||
return Utilities.<T>byteArrayToObject(decryptBytes(privateKey, data));
|
||||
}
|
||||
|
||||
public byte[] decryptBytes(PrivateKey privateKey, byte[] data) throws CryptoException {
|
||||
long ts = System.currentTimeMillis();
|
||||
|
||||
if (data.length < 25)
|
||||
throw new CryptoException("The data is shorter as the min. overhead length.");
|
||||
else if (data.length > MAX_SIZE)
|
||||
throw new CryptoException("The data exceeds the max. size.");
|
||||
|
||||
// 1 byte version | 20 byte checksum | 4 byte payload length | n bytes payload consisting of sig, encryptedOneTimeKey, cipherText
|
||||
byte[] version = new byte[1];
|
||||
int cursor = 0;
|
||||
System.arraycopy(data, cursor, version, 0, version.length);
|
||||
|
||||
if (!Arrays.equals(version, Version.NETWORK_PROTOCOL_VERSION))
|
||||
throw new CryptoException("Incorrect version.");
|
||||
|
||||
byte[] checksum = new byte[20];
|
||||
cursor += version.length;
|
||||
System.arraycopy(data, cursor, checksum, 0, checksum.length);
|
||||
|
||||
byte[] payloadLength = new byte[4];
|
||||
cursor += checksum.length;
|
||||
System.arraycopy(data, cursor, payloadLength, 0, payloadLength.length);
|
||||
int payloadLengthInt = ByteBuffer.wrap(payloadLength).order(ByteOrder.LITTLE_ENDIAN).getInt();
|
||||
log.debug("encode payloadLengthInt " + payloadLengthInt);
|
||||
if (payloadLengthInt < 0)
|
||||
throw new CryptoException("Payload length cannot be negative.");
|
||||
else if (payloadLengthInt > data.length - 25)
|
||||
throw new CryptoException("Payload length cannot be larger then data excluding overhead.");
|
||||
|
||||
byte[] payload = new byte[payloadLengthInt];
|
||||
cursor += payloadLength.length;
|
||||
System.arraycopy(data, cursor, payload, 0, payload.length);
|
||||
|
||||
|
||||
byte[] sigLength = new byte[4];
|
||||
// cursor stays the same
|
||||
System.arraycopy(data, cursor, sigLength, 0, sigLength.length);
|
||||
int sigLengthInt = ByteBuffer.wrap(sigLength).order(ByteOrder.LITTLE_ENDIAN).getInt();
|
||||
|
||||
byte[] sig = new byte[sigLengthInt];
|
||||
cursor += sigLength.length;
|
||||
System.arraycopy(data, cursor, sig, 0, sig.length);
|
||||
|
||||
byte[] signaturePubKey = new byte[33];
|
||||
cursor += sig.length;
|
||||
System.arraycopy(data, cursor, signaturePubKey, 0, signaturePubKey.length);
|
||||
|
||||
byte[] encryptedOneTimeKey = new byte[128];
|
||||
cursor += signaturePubKey.length;
|
||||
System.arraycopy(data, cursor, encryptedOneTimeKey, 0, encryptedOneTimeKey.length);
|
||||
|
||||
byte[] cipherText = new byte[payloadLengthInt - (sigLength.length + sig.length + signaturePubKey.length + encryptedOneTimeKey.length)];
|
||||
cursor += encryptedOneTimeKey.length;
|
||||
System.arraycopy(data, cursor, cipherText, 0, cipherText.length);
|
||||
|
||||
// Checksum
|
||||
byte[] controlChecksum = Utils.sha256hash160(Utilities.concatByteArrays(payloadLength, payload));
|
||||
if (!Arrays.equals(checksum, controlChecksum))
|
||||
throw new CryptoException("The checksum is invalid.");
|
||||
|
||||
try {
|
||||
// Decrypt oneTimeKey key with asymmetric key
|
||||
Cipher cipherAsym = Cipher.getInstance(CIPHER_ASYM);
|
||||
cipherAsym.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
byte[] oneTimeKey = cipherAsym.doFinal(encryptedOneTimeKey);
|
||||
|
||||
// Decrypt payload with symmetric key
|
||||
Key key = new SecretKeySpec(oneTimeKey, ALGO_SYM);
|
||||
Cipher cipherSym = Cipher.getInstance(CIPHER_SYM);
|
||||
cipherSym.init(Cipher.DECRYPT_MODE, key);
|
||||
byte[] plainText = cipherSym.doFinal(cipherText);
|
||||
|
||||
// Verify signature
|
||||
boolean verified = signatureService.verify(ECKey.fromPublicOnly(signaturePubKey).getPubKey(), plainText, ECKey.ECDSASignature.decodeFromDER(sig));
|
||||
if (!verified)
|
||||
throw new CryptoException("Signature is not valid");
|
||||
|
||||
log.debug("Encryption needed {} ms", System.currentTimeMillis() - ts);
|
||||
return plainText;
|
||||
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
|
||||
throw new CryptoException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
103
core/src/main/java/io/bitsquare/crypto/KeyRing.java
Normal file
103
core/src/main/java/io/bitsquare/crypto/KeyRing.java
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.crypto;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class KeyRing {
|
||||
private static final Logger log = LoggerFactory.getLogger(KeyRing.class);
|
||||
|
||||
// Public key is used as ID in DHT network. Used for data protection mechanism in TomP2P DHT
|
||||
private KeyPair dhtSignatureKeyPair;
|
||||
// Used for signing messages sent over the wire
|
||||
private KeyPair msgSignatureKeyPair;
|
||||
// Used for encrypting messages sent over the wire (hybrid encryption scheme is used, so it is used only to encrypt a symmetric session key)
|
||||
private KeyPair msgEncryptionKeyPair;
|
||||
|
||||
private PubKeyRing pubKeyRing;
|
||||
private final KeyStorage keyStorage;
|
||||
|
||||
@Inject
|
||||
public KeyRing(KeyStorage keyStorage) throws CryptoException {
|
||||
this.keyStorage = keyStorage;
|
||||
|
||||
init(keyStorage);
|
||||
}
|
||||
|
||||
// consider extra thread for loading (takes about 264 ms at first startup, then load only takes nearly no time)
|
||||
public void init(KeyStorage keyStorage) throws CryptoException {
|
||||
if (keyStorage.allKeyFilesExist()) {
|
||||
dhtSignatureKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.DHT_SIGNATURE);
|
||||
msgSignatureKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.MSG_SIGNATURE);
|
||||
msgEncryptionKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.MSG_ENCRYPTION);
|
||||
}
|
||||
else {
|
||||
// First time we create key pairs
|
||||
try {
|
||||
this.dhtSignatureKeyPair = CryptoService.generateDhtSignatureKeyPair();
|
||||
this.msgSignatureKeyPair = CryptoService.generateMsgSignatureKeyPair();
|
||||
this.msgEncryptionKeyPair = CryptoService.generateMsgEncryptionKeyPair();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
throw new CryptoException("Error at KeyRing constructor ", e);
|
||||
}
|
||||
keyStorage.saveKeyRing(this);
|
||||
}
|
||||
|
||||
pubKeyRing = new PubKeyRing(dhtSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic());
|
||||
}
|
||||
|
||||
// For unit testing
|
||||
KeyRing() throws NoSuchAlgorithmException {
|
||||
keyStorage = null;
|
||||
this.dhtSignatureKeyPair = CryptoService.generateDhtSignatureKeyPair();
|
||||
this.msgSignatureKeyPair = CryptoService.generateMsgSignatureKeyPair();
|
||||
this.msgEncryptionKeyPair = CryptoService.generateMsgEncryptionKeyPair();
|
||||
pubKeyRing = new PubKeyRing(dhtSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic());
|
||||
}
|
||||
|
||||
KeyRing(KeyPair dhtSignatureKeyPair, KeyPair msgSignatureKeyPair, KeyPair msgEncryptionKeyPair) {
|
||||
this.keyStorage = null;
|
||||
this.dhtSignatureKeyPair = dhtSignatureKeyPair;
|
||||
this.msgSignatureKeyPair = msgSignatureKeyPair;
|
||||
this.msgEncryptionKeyPair = msgEncryptionKeyPair;
|
||||
pubKeyRing = new PubKeyRing(dhtSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic());
|
||||
}
|
||||
|
||||
public KeyPair getDhtSignatureKeyPair() {
|
||||
return dhtSignatureKeyPair;
|
||||
}
|
||||
|
||||
public KeyPair getMsgSignatureKeyPair() {
|
||||
return msgSignatureKeyPair;
|
||||
}
|
||||
|
||||
public KeyPair getMsgEncryptionKeyPair() {
|
||||
return msgEncryptionKeyPair;
|
||||
}
|
||||
|
||||
public PubKeyRing getPubKeyRing() {
|
||||
return pubKeyRing;
|
||||
}
|
||||
}
|
171
core/src/main/java/io/bitsquare/crypto/KeyStorage.java
Normal file
171
core/src/main/java/io/bitsquare/crypto/KeyStorage.java
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.crypto;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Security;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
public class KeyStorage {
|
||||
private static final Logger log = LoggerFactory.getLogger(KeyStorage.class);
|
||||
|
||||
public static final String DIR_KEY = "key.storage.dir";
|
||||
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
|
||||
public enum Key {
|
||||
DHT_SIGNATURE("dhtSignature", CryptoService.DHT_SIGN_KEY_ALGO),
|
||||
MSG_SIGNATURE("msgSignature", CryptoService.MSG_SIGN_KEY_ALGO),
|
||||
MSG_ENCRYPTION("msgEncryption", CryptoService.MSG_ENCR_KEY_ALGO);
|
||||
|
||||
private final String fileName;
|
||||
private final String algorithm;
|
||||
|
||||
Key(String fileName, String algorithm) {
|
||||
this.fileName = fileName;
|
||||
this.algorithm = algorithm;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Key{" +
|
||||
"fileName='" + fileName + '\'' +
|
||||
", algorithm='" + algorithm + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
private final File storageDir;
|
||||
|
||||
@Inject
|
||||
public KeyStorage(@Named(DIR_KEY) File storageDir) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException {
|
||||
this.storageDir = storageDir;
|
||||
}
|
||||
|
||||
public boolean allKeyFilesExist() throws CryptoException {
|
||||
return fileExists(KeyStorage.Key.DHT_SIGNATURE) && fileExists(KeyStorage.Key.MSG_SIGNATURE) && fileExists(KeyStorage.Key.MSG_ENCRYPTION);
|
||||
}
|
||||
|
||||
private boolean fileExists(Key key) throws CryptoException {
|
||||
return new File(storageDir + "/" + key.getFileName() + "Pub.key").exists();
|
||||
}
|
||||
|
||||
public KeyPair loadKeyPair(Key key) throws CryptoException {
|
||||
long now = System.currentTimeMillis();
|
||||
try {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
|
||||
PublicKey publicKey;
|
||||
PrivateKey privateKey;
|
||||
|
||||
File filePublicKey = new File(storageDir + "/" + key.getFileName() + "Pub.key");
|
||||
try (FileInputStream fis = new FileInputStream(filePublicKey.getPath())) {
|
||||
byte[] encodedPublicKey = new byte[(int) filePublicKey.length()];
|
||||
fis.read(encodedPublicKey);
|
||||
|
||||
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedPublicKey);
|
||||
publicKey = keyFactory.generatePublic(publicKeySpec);
|
||||
} catch (InvalidKeySpecException | IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
throw new CryptoException("Could not load key " + key.toString(), e);
|
||||
}
|
||||
|
||||
File filePrivateKey = new File(storageDir + "/" + key.getFileName() + "Priv.key");
|
||||
try (FileInputStream fis = new FileInputStream(filePrivateKey.getPath())) {
|
||||
byte[] encodedPrivateKey = new byte[(int) filePrivateKey.length()];
|
||||
fis.read(encodedPrivateKey);
|
||||
|
||||
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
|
||||
privateKey = keyFactory.generatePrivate(privateKeySpec);
|
||||
} catch (InvalidKeySpecException | IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
throw new CryptoException("Could not load key " + key.toString(), e);
|
||||
}
|
||||
log.info("load completed in {} msec", System.currentTimeMillis() - now);
|
||||
return new KeyPair(publicKey, privateKey);
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
throw new CryptoException("Could not load key " + key.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveKeyRing(KeyRing keyRing) throws CryptoException {
|
||||
saveKeyPair(keyRing.getDhtSignatureKeyPair(), Key.DHT_SIGNATURE.getFileName());
|
||||
saveKeyPair(keyRing.getMsgSignatureKeyPair(), Key.MSG_SIGNATURE.getFileName());
|
||||
saveKeyPair(keyRing.getMsgEncryptionKeyPair(), Key.MSG_ENCRYPTION.getFileName());
|
||||
}
|
||||
|
||||
public void saveKeyPair(KeyPair keyPair, String name) throws CryptoException {
|
||||
if (!storageDir.exists())
|
||||
storageDir.mkdir();
|
||||
|
||||
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyPair.getPublic().getEncoded());
|
||||
try (FileOutputStream fos = new FileOutputStream(storageDir + "/" + name + "Pub.key")) {
|
||||
fos.write(x509EncodedKeySpec.getEncoded());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
throw new CryptoException("Could not save key " + name, e);
|
||||
}
|
||||
|
||||
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded());
|
||||
try (FileOutputStream fos = new FileOutputStream(storageDir + "/" + name + "Priv.key")) {
|
||||
fos.write(pkcs8EncodedKeySpec.getEncoded());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
throw new CryptoException("Could not save key " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,27 +15,32 @@
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.p2p;
|
||||
package io.bitsquare.crypto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import io.bitsquare.p2p.Message;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/*
|
||||
Stores a message in encrypted form, so it never leaves the client in plain text.
|
||||
*/
|
||||
public class EncryptedMailboxMessage implements MailboxMessage, Serializable {
|
||||
private static final long serialVersionUID = -3111178895546299769L;
|
||||
private static final Logger log = LoggerFactory.getLogger(EncryptedMailboxMessage.class);
|
||||
public class MessageWithPubKey implements Message {
|
||||
private static final Logger log = LoggerFactory.getLogger(MessageWithPubKey.class);
|
||||
|
||||
private final byte[] bytes;
|
||||
|
||||
public EncryptedMailboxMessage(byte[] bytes) {
|
||||
this.bytes = bytes;
|
||||
public Message getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return bytes;
|
||||
public PublicKey getSignaturePubKey() {
|
||||
return signaturePubKey;
|
||||
}
|
||||
|
||||
private final Message message;
|
||||
private final PublicKey signaturePubKey;
|
||||
|
||||
|
||||
public MessageWithPubKey(Message message, PublicKey signaturePubKey) {
|
||||
this.message = message;
|
||||
this.signaturePubKey = signaturePubKey;
|
||||
}
|
||||
}
|
142
core/src/main/java/io/bitsquare/crypto/PubKeyRing.java
Normal file
142
core/src/main/java/io/bitsquare/crypto/PubKeyRing.java
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.crypto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Same as KeyRing but with public keys only.
|
||||
* Used to sent over the wire to other peer.
|
||||
*/
|
||||
public class PubKeyRing implements Serializable {
|
||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PubKeyRing.class);
|
||||
|
||||
private final byte[] dhtSignaturePubKeyBytes;
|
||||
private final byte[] msgSignaturePubKeyBytes;
|
||||
private final byte[] msgEncryptionPubKeyBytes;
|
||||
|
||||
transient private PublicKey dhtSignaturePubKey;
|
||||
transient private PublicKey msgSignaturePubKey;
|
||||
transient private PublicKey msgEncryptionPubKey;
|
||||
|
||||
public PubKeyRing(PublicKey dhtSignaturePubKey, PublicKey msgSignaturePubKey, PublicKey msgEncryptionPubKey) {
|
||||
this.dhtSignaturePubKey = dhtSignaturePubKey;
|
||||
this.msgSignaturePubKey = msgSignaturePubKey;
|
||||
this.msgEncryptionPubKey = msgEncryptionPubKey;
|
||||
|
||||
this.dhtSignaturePubKeyBytes = new X509EncodedKeySpec(dhtSignaturePubKey.getEncoded()).getEncoded();
|
||||
this.msgSignaturePubKeyBytes = new X509EncodedKeySpec(msgSignaturePubKey.getEncoded()).getEncoded();
|
||||
this.msgEncryptionPubKeyBytes = new X509EncodedKeySpec(msgEncryptionPubKey.getEncoded()).getEncoded();
|
||||
}
|
||||
|
||||
public PublicKey getDhtSignaturePubKey() {
|
||||
if (dhtSignaturePubKey == null) {
|
||||
try {
|
||||
dhtSignaturePubKey = KeyFactory.getInstance(CryptoService.DHT_SIGN_KEY_ALGO).generatePublic(new X509EncodedKeySpec(dhtSignaturePubKeyBytes));
|
||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
return dhtSignaturePubKey;
|
||||
}
|
||||
|
||||
|
||||
public PublicKey getMsgSignaturePubKey() {
|
||||
if (msgSignaturePubKey == null) {
|
||||
try {
|
||||
msgSignaturePubKey = KeyFactory.getInstance(CryptoService.MSG_SIGN_KEY_ALGO).generatePublic(new X509EncodedKeySpec(msgSignaturePubKeyBytes));
|
||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
return msgSignaturePubKey;
|
||||
}
|
||||
|
||||
public PublicKey getMsgEncryptionPubKey() {
|
||||
if (msgEncryptionPubKey == null) {
|
||||
try {
|
||||
msgEncryptionPubKey = KeyFactory.getInstance(CryptoService.MSG_ENCR_KEY_ALGO).generatePublic(new X509EncodedKeySpec(msgEncryptionPubKeyBytes));
|
||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
return msgEncryptionPubKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
PubKeyRing that = (PubKeyRing) o;
|
||||
|
||||
if (!Arrays.equals(dhtSignaturePubKeyBytes, that.dhtSignaturePubKeyBytes)) return false;
|
||||
if (!Arrays.equals(msgSignaturePubKeyBytes, that.msgSignaturePubKeyBytes)) return false;
|
||||
return Arrays.equals(msgEncryptionPubKeyBytes, that.msgEncryptionPubKeyBytes);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = dhtSignaturePubKeyBytes != null ? Arrays.hashCode(dhtSignaturePubKeyBytes) : 0;
|
||||
result = 31 * result + (msgSignaturePubKeyBytes != null ? Arrays.hashCode(msgSignaturePubKeyBytes) : 0);
|
||||
result = 31 * result + (msgEncryptionPubKeyBytes != null ? Arrays.hashCode(msgEncryptionPubKeyBytes) : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public String getHashString() {
|
||||
try {
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
|
||||
messageDigest.update(dhtSignaturePubKeyBytes);
|
||||
messageDigest.update(msgSignaturePubKeyBytes);
|
||||
messageDigest.update(msgEncryptionPubKeyBytes);
|
||||
return new String(messageDigest.digest());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Hash Algorithm not found.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PubKeyRing{" +
|
||||
"\ndhtSignaturePubKey=\n" + Util.pubKeyToString(getDhtSignaturePubKey()) +
|
||||
"\n\nmsgSignaturePubKey=\n" + Util.pubKeyToString(getMsgSignaturePubKey()) +
|
||||
"\n\nmsgEncryptionPubKey=\n" + Util.pubKeyToString(getMsgEncryptionPubKey()) +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.crypto;
|
||||
|
||||
import io.bitsquare.p2p.Message;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.SealedObject;
|
||||
|
||||
/**
|
||||
* Packs the encrypted symmetric secretKey and the encrypted signed message into one object.
|
||||
* SecretKey is encrypted with asymmetric pubKey of peer. Signed message is encrypted with secretKey.
|
||||
* Using that hybrid encryption model we are not restricted by data size and performance as symmetric encryption is very fast.
|
||||
*/
|
||||
public class SealedAndSignedMessage implements Serializable, Message {
|
||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SealedAndSignedMessage.class);
|
||||
|
||||
private final SealedObject sealedSecretKey;
|
||||
private final SealedObject sealedMessage;
|
||||
private final PublicKey signaturePubKey;
|
||||
|
||||
public SealedAndSignedMessage(SealedObject sealedSecretKey, SealedObject sealedMessage, PublicKey signaturePubKey) {
|
||||
this.sealedSecretKey = sealedSecretKey;
|
||||
this.sealedMessage = sealedMessage;
|
||||
this.signaturePubKey = signaturePubKey;
|
||||
}
|
||||
|
||||
public SealedObject getSealedSecretKey() {
|
||||
return sealedSecretKey;
|
||||
}
|
||||
|
||||
public SealedObject getSealedMessage() {
|
||||
return sealedMessage;
|
||||
}
|
||||
|
||||
public PublicKey getSignaturePubKey() {
|
||||
return signaturePubKey;
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.crypto;
|
||||
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Utils;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import org.spongycastle.util.encoders.Base64;
|
||||
|
||||
public class SignatureService {
|
||||
|
||||
public String signMessage(ECKey key, String message) {
|
||||
byte[] data = Utils.formatMessageForSigning(message);
|
||||
Sha256Hash hash = Sha256Hash.hashTwice(data);
|
||||
return signMessage(key, hash);
|
||||
}
|
||||
|
||||
public ECKey.ECDSASignature signBytes(ECKey key, byte[] data) {
|
||||
return key.sign(Sha256Hash.hashTwice(data), null);
|
||||
}
|
||||
|
||||
public String signMessage(ECKey key, byte[] data) {
|
||||
Sha256Hash hash = Sha256Hash.hashTwice(data);
|
||||
return signMessage(key, hash);
|
||||
}
|
||||
|
||||
public String signMessage(ECKey key, Sha256Hash hash) {
|
||||
ECKey.ECDSASignature sig = key.sign(hash, null);
|
||||
// Now we have to work backwards to figure out the recId needed to recover the signature.
|
||||
int recId = -1;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ECKey k = ECKey.recoverFromSignature(i, sig, hash, key.isCompressed());
|
||||
if (k != null && k.getPubKeyPoint().equals(key.getPubKeyPoint())) {
|
||||
recId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (recId == -1)
|
||||
throw new RuntimeException("Could not construct a recoverable key. This should never happen.");
|
||||
int headerByte = recId + 27 + (key.isCompressed() ? 4 : 0);
|
||||
byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 bytes for S
|
||||
sigData[0] = (byte) headerByte;
|
||||
System.arraycopy(Utils.bigIntegerToBytes(sig.r, 32), 0, sigData, 1, 32);
|
||||
System.arraycopy(Utils.bigIntegerToBytes(sig.s, 32), 0, sigData, 33, 32);
|
||||
return new String(Base64.encode(sigData), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
|
||||
public byte[] digestMessageWithSignature(ECKey key, String message) {
|
||||
String signedMessage = signMessage(key, message);
|
||||
return Utils.sha256hash160(message.concat(signedMessage).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
public boolean verify(byte[] signaturePubKey, byte[] data, ECKey.ECDSASignature sig) {
|
||||
return ECKey.fromPublicOnly(signaturePubKey).verify(Sha256Hash.hashTwice(data), sig);
|
||||
}
|
||||
}
|
@ -25,24 +25,23 @@ import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Util {
|
||||
private static final Logger log = LoggerFactory.getLogger(Util.class);
|
||||
|
||||
public static String getHexFromPubKey(PublicKey publicKey) {
|
||||
public static String pubKeyToString(PublicKey publicKey) {
|
||||
final X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
|
||||
return Utils.HEX.encode(x509EncodedKeySpec.getEncoded());
|
||||
return Base64.getEncoder().encodeToString(x509EncodedKeySpec.getEncoded());
|
||||
}
|
||||
|
||||
// TODO just temp for arbitrator
|
||||
public static PublicKey decodeDSAPubKeyHex(String pubKeyHex) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Utils.HEX.decode(pubKeyHex));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
|
||||
return keyFactory.generatePublic(pubKeySpec);
|
||||
}
|
||||
|
||||
public static String encodePubKeyToHex(PublicKey pubKey) {
|
||||
return Utils.HEX.encode(pubKey.getEncoded());
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
package io.bitsquare.offer;
|
||||
|
||||
import io.bitsquare.btc.Restrictions;
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.locale.Country;
|
||||
|
||||
@ -28,8 +29,6 @@ import org.bitcoinj.utils.Fiat;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@ -48,6 +47,7 @@ public class Offer implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private transient static final Logger log = LoggerFactory.getLogger(Offer.class);
|
||||
|
||||
|
||||
public enum Direction {BUY, SELL}
|
||||
|
||||
public enum State {
|
||||
@ -71,7 +71,7 @@ public class Offer implements Serializable {
|
||||
private final long fiatPrice;
|
||||
private final Coin amount;
|
||||
private final Coin minAmount;
|
||||
private final PublicKey p2pSigPubKey;
|
||||
private final PubKeyRing pubKeyRing;
|
||||
private final FiatAccount.Type fiatAccountType;
|
||||
private final Country bankAccountCountry;
|
||||
|
||||
@ -95,7 +95,7 @@ public class Offer implements Serializable {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Offer(String id,
|
||||
PublicKey p2pSigPubKey,
|
||||
PubKeyRing pubKeyRing,
|
||||
Direction direction,
|
||||
long fiatPrice,
|
||||
Coin amount,
|
||||
@ -109,7 +109,7 @@ public class Offer implements Serializable {
|
||||
List<Country> acceptedCountries,
|
||||
List<String> acceptedLanguageCodes) {
|
||||
this.id = id;
|
||||
this.p2pSigPubKey = p2pSigPubKey;
|
||||
this.pubKeyRing = pubKeyRing;
|
||||
this.direction = direction;
|
||||
this.fiatPrice = fiatPrice;
|
||||
this.amount = amount;
|
||||
@ -144,7 +144,7 @@ public class Offer implements Serializable {
|
||||
checkNotNull(getCurrencyCode(), "Currency is null");
|
||||
checkNotNull(getDirection(), "Direction is null");
|
||||
checkNotNull(getId(), "Id is null");
|
||||
checkNotNull(getP2pSigPubKey(), "p2pSigPubKey is null");
|
||||
checkNotNull(getPubKeyRing(), "pubKeyRing is null");
|
||||
checkNotNull(getMinAmount(), "MinAmount is null");
|
||||
checkNotNull(getPrice(), "Price is null");
|
||||
|
||||
@ -199,6 +199,11 @@ public class Offer implements Serializable {
|
||||
return id;
|
||||
}
|
||||
|
||||
public PubKeyRing getPubKeyRing() {
|
||||
return pubKeyRing;
|
||||
}
|
||||
|
||||
|
||||
public Fiat getPrice() {
|
||||
return Fiat.valueOf(currencyCode, fiatPrice);
|
||||
}
|
||||
@ -256,10 +261,6 @@ public class Offer implements Serializable {
|
||||
return bankAccountUID;
|
||||
}
|
||||
|
||||
public PublicKey getP2pSigPubKey() {
|
||||
return p2pSigPubKey;
|
||||
}
|
||||
|
||||
public Date getCreationDate() {
|
||||
return creationDate;
|
||||
}
|
||||
@ -282,7 +283,7 @@ public class Offer implements Serializable {
|
||||
", fiatPrice=" + fiatPrice +
|
||||
", amount=" + amount +
|
||||
", minAmount=" + minAmount +
|
||||
/* ", p2pSigPubKey=" + p2pSigPubKey +*/
|
||||
/* ", pubKeyRing=" + pubKeyRing +*/
|
||||
", fiatAccountType=" + fiatAccountType +
|
||||
", bankAccountCountry=" + bankAccountCountry +
|
||||
", securityDeposit=" + securityDeposit +
|
||||
|
@ -19,11 +19,11 @@ package io.bitsquare.offer.tomp2p;
|
||||
|
||||
import io.bitsquare.common.handlers.FaultHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.crypto.KeyRing;
|
||||
import io.bitsquare.offer.Offer;
|
||||
import io.bitsquare.offer.OfferBookService;
|
||||
import io.bitsquare.p2p.tomp2p.TomP2PDHTService;
|
||||
import io.bitsquare.p2p.tomp2p.TomP2PNode;
|
||||
import io.bitsquare.user.User;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@ -58,8 +58,8 @@ public class TomP2POfferBookService extends TomP2PDHTService implements OfferBoo
|
||||
|
||||
|
||||
@Inject
|
||||
public TomP2POfferBookService(TomP2PNode tomP2PNode, User user) {
|
||||
super(tomP2PNode, user);
|
||||
public TomP2POfferBookService(TomP2PNode tomP2PNode, KeyRing keyRing) {
|
||||
super(tomP2PNode, keyRing);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -18,10 +18,9 @@
|
||||
package io.bitsquare.p2p;
|
||||
|
||||
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.p2p.listener.GetPeerAddressListener;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
public interface AddressService extends DHTService {
|
||||
void findPeerAddress(PublicKey p2pSigPubKey, GetPeerAddressListener getPeerAddressListener);
|
||||
void findPeerAddress(PubKeyRing pubKeyRing, GetPeerAddressListener getPeerAddressListener);
|
||||
}
|
||||
|
@ -15,15 +15,10 @@
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.crypto;
|
||||
package io.bitsquare.p2p;
|
||||
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Utils;
|
||||
import io.bitsquare.crypto.MessageWithPubKey;
|
||||
|
||||
public class HashService {
|
||||
|
||||
public Sha256Hash hash(String message) {
|
||||
byte[] data = Utils.formatMessageForSigning(message);
|
||||
return Sha256Hash.hashTwice(data);
|
||||
}
|
||||
public interface DecryptedMessageHandler {
|
||||
void handleMessage(MessageWithPubKey message, Peer sender);
|
||||
}
|
@ -17,8 +17,10 @@
|
||||
|
||||
package io.bitsquare.p2p;
|
||||
|
||||
import io.bitsquare.crypto.SealedAndSignedMessage;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MailboxMessagesResultHandler {
|
||||
void handleResult(List<EncryptedMailboxMessage> encryptedMailboxMessages);
|
||||
void handleResult(List<SealedAndSignedMessage> encryptedMessages);
|
||||
}
|
||||
|
@ -19,14 +19,14 @@ package io.bitsquare.p2p;
|
||||
|
||||
import io.bitsquare.common.handlers.FaultHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.crypto.SealedAndSignedMessage;
|
||||
|
||||
public interface MailboxService {
|
||||
void addMessage(PublicKey recipientP2pSigPubKey, EncryptedMailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler);
|
||||
void addMessage(PubKeyRing pubKeyRing, SealedAndSignedMessage message, ResultHandler resultHandler, FaultHandler faultHandler);
|
||||
|
||||
void getAllMessages(PublicKey p2pSigPubKey, MailboxMessagesResultHandler resultHandler);
|
||||
void getAllMessages(MailboxMessagesResultHandler resultHandler);
|
||||
|
||||
void removeAllMessages(PublicKey p2pSigPubKey, ResultHandler resultHandler, FaultHandler faultHandler);
|
||||
void removeAllMessages(ResultHandler resultHandler, FaultHandler faultHandler);
|
||||
|
||||
}
|
||||
|
@ -18,20 +18,20 @@
|
||||
package io.bitsquare.p2p;
|
||||
|
||||
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.p2p.listener.SendMessageListener;
|
||||
|
||||
import org.bitcoinj.core.ECKey;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
public interface MessageService extends P2PService {
|
||||
|
||||
void sendMessage(Peer peer, Message message, SendMessageListener listener);
|
||||
|
||||
void sendMessage(Peer peer, Message message, PublicKey recipientP2pSigPubKey, PublicKey recipientP2pEncryptPubKey, ECKey registrationKeyPair,
|
||||
SendMessageListener listener);
|
||||
void sendEncryptedMessage(Peer peer, PubKeyRing pubKeyRing, Message message, SendMessageListener listener);
|
||||
|
||||
void addMessageHandler(MessageHandler listener);
|
||||
|
||||
void removeMessageHandler(MessageHandler listener);
|
||||
}
|
||||
|
||||
void addDecryptedMessageHandler(DecryptedMessageHandler listener);
|
||||
|
||||
void removeDecryptedMessageHandler(DecryptedMessageHandler listener);
|
||||
}
|
@ -17,16 +17,15 @@
|
||||
|
||||
package io.bitsquare.p2p.tomp2p;
|
||||
|
||||
import io.bitsquare.crypto.KeyRing;
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.p2p.AddressService;
|
||||
import io.bitsquare.p2p.NetworkException;
|
||||
import io.bitsquare.p2p.Peer;
|
||||
import io.bitsquare.p2p.listener.GetPeerAddressListener;
|
||||
import io.bitsquare.user.User;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
@ -63,10 +62,10 @@ public class TomP2PAddressService extends TomP2PDHTService implements AddressSer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public TomP2PAddressService(TomP2PNode tomP2PNode, User user) {
|
||||
super(tomP2PNode, user);
|
||||
public TomP2PAddressService(TomP2PNode tomP2PNode, KeyRing keyRing) {
|
||||
super(tomP2PNode, keyRing);
|
||||
|
||||
locationKey = Utils.makeSHAHash(user.getP2pSigKeyPair().getPublic().getEncoded());
|
||||
locationKey = Utils.makeSHAHash(keyRing.getPubKeyRing().getDhtSignaturePubKey().getEncoded());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -92,9 +91,9 @@ public class TomP2PAddressService extends TomP2PDHTService implements AddressSer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void findPeerAddress(PublicKey p2pSigPubKey, GetPeerAddressListener listener) {
|
||||
final Number160 locationKey = Utils.makeSHAHash(p2pSigPubKey.getEncoded());
|
||||
FutureGet futureGet = getDataOfProtectedDomain(locationKey, p2pSigPubKey);
|
||||
public void findPeerAddress(PubKeyRing pubKeyRing, GetPeerAddressListener listener) {
|
||||
final Number160 locationKey = Utils.makeSHAHash(pubKeyRing.getDhtSignaturePubKey().getEncoded());
|
||||
FutureGet futureGet = getDataOfProtectedDomain(locationKey, pubKeyRing.getDhtSignaturePubKey());
|
||||
log.trace("findPeerAddress called");
|
||||
futureGet.addListener(new BaseFutureAdapter<BaseFuture>() {
|
||||
@Override
|
||||
|
@ -17,8 +17,8 @@
|
||||
|
||||
package io.bitsquare.p2p.tomp2p;
|
||||
|
||||
import io.bitsquare.crypto.KeyRing;
|
||||
import io.bitsquare.p2p.DHTService;
|
||||
import io.bitsquare.user.User;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
@ -38,7 +38,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TomP2PDHTService extends TomP2PService implements DHTService {
|
||||
private static final Logger log = LoggerFactory.getLogger(TomP2PDHTService.class);
|
||||
private final KeyPair keyPair;
|
||||
private final KeyPair dhtSignatureKeyPair;
|
||||
private final Number160 pubKeyHashForMyDomain;
|
||||
|
||||
|
||||
@ -47,10 +47,11 @@ public class TomP2PDHTService extends TomP2PService implements DHTService {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public TomP2PDHTService(TomP2PNode tomP2PNode, User user) {
|
||||
public TomP2PDHTService(TomP2PNode tomP2PNode, KeyRing keyRing) {
|
||||
super(tomP2PNode);
|
||||
keyPair = user.getP2pSigKeyPair();
|
||||
pubKeyHashForMyDomain = Utils.makeSHAHash(keyPair.getPublic().getEncoded());
|
||||
|
||||
dhtSignatureKeyPair = keyRing.getDhtSignatureKeyPair();
|
||||
pubKeyHashForMyDomain = Utils.makeSHAHash(dhtSignatureKeyPair.getPublic().getEncoded());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -117,7 +118,7 @@ public class TomP2PDHTService extends TomP2PService implements DHTService {
|
||||
*/
|
||||
public FuturePut putDataToMyProtectedDomain(Number160 locationKey, Data data) {
|
||||
log.trace("putDataToMyProtectedDomain");
|
||||
data.protectEntry(keyPair);
|
||||
data.protectEntry(dhtSignatureKeyPair);
|
||||
return peerDHT.put(locationKey).data(data).protectDomain().domainKey(pubKeyHashForMyDomain).start();
|
||||
}
|
||||
|
||||
@ -131,7 +132,7 @@ public class TomP2PDHTService extends TomP2PService implements DHTService {
|
||||
public FutureRemove removeDataFromMyProtectedDomain(Number160 locationKey) {
|
||||
log.trace("removeDataOfProtectedDomain");
|
||||
if (peerDHT != null)
|
||||
return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(keyPair).start();
|
||||
return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(dhtSignatureKeyPair).start();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
@ -167,10 +168,10 @@ public class TomP2PDHTService extends TomP2PService implements DHTService {
|
||||
*/
|
||||
public FuturePut addProtectedDataToMap(Number160 locationKey, Data data) {
|
||||
log.trace("addProtectedDataToMap locationKey = " + locationKey);
|
||||
data.protectEntry(keyPair);
|
||||
data.protectEntry(dhtSignatureKeyPair);
|
||||
log.trace("addProtectedDataToMap with contentKey " + data.hash().toString());
|
||||
|
||||
return peerDHT.add(locationKey).data(data).keyPair(keyPair).start();
|
||||
return peerDHT.add(locationKey).data(data).keyPair(dhtSignatureKeyPair).start();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -185,7 +186,7 @@ public class TomP2PDHTService extends TomP2PService implements DHTService {
|
||||
log.trace("removeProtectedDataFromMap locationKey = " + locationKey);
|
||||
Number160 contentKey = data.hash();
|
||||
log.trace("removeProtectedDataFromMap with contentKey " + contentKey.toString());
|
||||
return peerDHT.remove(locationKey).contentKey(contentKey).keyPair(keyPair).start();
|
||||
return peerDHT.remove(locationKey).contentKey(contentKey).keyPair(dhtSignatureKeyPair).start();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -221,8 +222,8 @@ public class TomP2PDHTService extends TomP2PService implements DHTService {
|
||||
log.trace("addDataToMapOfProtectedDomain");
|
||||
log.trace("addDataToMapOfProtectedDomain with contentKey " + data.hash().toString());
|
||||
final Number160 pubKeyHashOfDomainOwner = Utils.makeSHAHash(publicKey.getEncoded());
|
||||
return peerDHT.add(locationKey).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(keyPair)
|
||||
.data(data).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(keyPair).start();
|
||||
return peerDHT.add(locationKey).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(dhtSignatureKeyPair)
|
||||
.data(data).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(dhtSignatureKeyPair).start();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,7 +238,7 @@ public class TomP2PDHTService extends TomP2PService implements DHTService {
|
||||
log.trace("removeDataFromMapOfMyProtectedDomain");
|
||||
Number160 contentKey = data.hash();
|
||||
log.trace("removeDataFromMapOfMyProtectedDomain with contentKey " + contentKey.toString());
|
||||
return peerDHT.remove(locationKey).contentKey(contentKey).domainKey(pubKeyHashForMyDomain).keyPair(keyPair).start();
|
||||
return peerDHT.remove(locationKey).contentKey(contentKey).domainKey(pubKeyHashForMyDomain).keyPair(dhtSignatureKeyPair).start();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -261,7 +262,8 @@ public class TomP2PDHTService extends TomP2PService implements DHTService {
|
||||
*/
|
||||
public FutureRemove removeAllDataFromMapOfMyProtectedDomain(Number160 locationKey) {
|
||||
log.trace("getDataFromMapOfMyProtectedDomain");
|
||||
return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(keyPair).all().domainKey(pubKeyHashForMyDomain).keyPair(keyPair).start();
|
||||
return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(dhtSignatureKeyPair).all().domainKey(pubKeyHashForMyDomain).keyPair
|
||||
(dhtSignatureKeyPair).start();
|
||||
}
|
||||
|
||||
|
||||
|
@ -19,14 +19,16 @@ package io.bitsquare.p2p.tomp2p;
|
||||
|
||||
import io.bitsquare.common.handlers.FaultHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.crypto.KeyRing;
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.crypto.SealedAndSignedMessage;
|
||||
import io.bitsquare.offer.OfferBookService;
|
||||
import io.bitsquare.p2p.EncryptedMailboxMessage;
|
||||
import io.bitsquare.p2p.MailboxMessagesResultHandler;
|
||||
import io.bitsquare.p2p.MailboxService;
|
||||
import io.bitsquare.user.User;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -53,10 +55,13 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer
|
||||
private static final int TTL = 21 * 24 * 60 * 60; // the message is default 21 days valid, as a max trade period might be about 2 weeks.
|
||||
|
||||
private final List<OfferBookService.Listener> offerRepositoryListeners = new ArrayList<>();
|
||||
private final KeyPair dhtSignatureKeyPair;
|
||||
|
||||
@Inject
|
||||
public TomP2PMailboxService(TomP2PNode tomP2PNode, User user) {
|
||||
super(tomP2PNode, user);
|
||||
public TomP2PMailboxService(TomP2PNode tomP2PNode, KeyRing keyRing) {
|
||||
super(tomP2PNode, keyRing);
|
||||
|
||||
dhtSignatureKeyPair = keyRing.getDhtSignatureKeyPair();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,25 +75,33 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMessage(PublicKey recipientP2pSigPubKey, EncryptedMailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler) {
|
||||
public void addMessage(PubKeyRing pubKeyRing, SealedAndSignedMessage message, ResultHandler resultHandler, FaultHandler faultHandler) {
|
||||
try {
|
||||
final Data data = new Data(message);
|
||||
data.ttlSeconds(TTL);
|
||||
log.trace("Add message to DHT requested. Added data: [locationKey: " + getLocationKey(recipientP2pSigPubKey) +
|
||||
Number160 locationKey = getLocationKey(pubKeyRing.getDhtSignaturePubKey());
|
||||
log.trace("Add message to DHT requested. Added data: [locationKey: " + locationKey +
|
||||
", hash: " + data.hash().toString() + "]");
|
||||
|
||||
FuturePut futurePut = addDataToMapOfProtectedDomain(getLocationKey(recipientP2pSigPubKey), data, recipientP2pSigPubKey);
|
||||
FuturePut futurePut = addDataToMapOfProtectedDomain(locationKey,
|
||||
data, pubKeyRing.getDhtSignaturePubKey());
|
||||
futurePut.addListener(new BaseFutureListener<BaseFuture>() {
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
executor.execute(() -> {
|
||||
log.trace("Add message to mailbox was successful. Added data: [locationKey: " + locationKey + ", value: " + data + "]");
|
||||
resultHandler.handleResult();
|
||||
|
||||
log.trace("Add message to mailbox was successful. Added data: [locationKey: " + getLocationKey(recipientP2pSigPubKey) +
|
||||
", value: " + data + "]");
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Seems to be a bug in TomP2P that when one peer shuts down the expected nr of peers and the delivered are not matching
|
||||
// As far tested the storage succeeded, so seems to be a wrong message.
|
||||
//Future (compl/canc):true/false, FAILED, Expected 3 result, but got 2
|
||||
|
||||
log.warn("Ignoring isSuccess=false case. failedReason: {}", future.failedReason());
|
||||
resultHandler.handleResult();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -102,21 +115,21 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getAllMessages(PublicKey p2pSigPubKey, MailboxMessagesResultHandler resultHandler) {
|
||||
log.trace("Get messages from DHT requested for locationKey: " + getLocationKey(p2pSigPubKey));
|
||||
FutureGet futureGet = getDataFromMapOfMyProtectedDomain(getLocationKey(p2pSigPubKey));
|
||||
public void getAllMessages(MailboxMessagesResultHandler resultHandler) {
|
||||
log.trace("Get messages from DHT requested for locationKey: " + getLocationKey(dhtSignatureKeyPair.getPublic()));
|
||||
FutureGet futureGet = getDataFromMapOfMyProtectedDomain(getLocationKey(dhtSignatureKeyPair.getPublic()));
|
||||
futureGet.addListener(new BaseFutureAdapter<BaseFuture>() {
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
final Map<Number640, Data> dataMap = futureGet.dataMap();
|
||||
List<EncryptedMailboxMessage> messages = new ArrayList<>();
|
||||
List<SealedAndSignedMessage> messages = new ArrayList<>();
|
||||
if (dataMap != null) {
|
||||
for (Data messageData : dataMap.values()) {
|
||||
try {
|
||||
Object messageDataObject = messageData.object();
|
||||
if (messageDataObject instanceof EncryptedMailboxMessage) {
|
||||
messages.add((EncryptedMailboxMessage) messageDataObject);
|
||||
if (messageDataObject instanceof SealedAndSignedMessage) {
|
||||
messages.add((SealedAndSignedMessage) messageDataObject);
|
||||
}
|
||||
} catch (ClassNotFoundException | IOException e) {
|
||||
e.printStackTrace();
|
||||
@ -125,7 +138,7 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer
|
||||
executor.execute(() -> resultHandler.handleResult(messages));
|
||||
}
|
||||
|
||||
log.trace("Get messages from DHT was successful. Stored data: [key: " + getLocationKey(p2pSigPubKey)
|
||||
log.trace("Get messages from DHT was successful. Stored data: [key: " + getLocationKey(dhtSignatureKeyPair.getPublic())
|
||||
+ ", values: " + futureGet.dataMap() + "]");
|
||||
}
|
||||
else {
|
||||
@ -143,9 +156,9 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllMessages(PublicKey p2pSigPubKey, ResultHandler resultHandler, FaultHandler faultHandler) {
|
||||
log.trace("Remove all messages from DHT requested. locationKey: " + getLocationKey(p2pSigPubKey));
|
||||
FutureRemove futureRemove = removeAllDataFromMapOfMyProtectedDomain(getLocationKey(p2pSigPubKey));
|
||||
public void removeAllMessages(ResultHandler resultHandler, FaultHandler faultHandler) {
|
||||
log.trace("Remove all messages from DHT requested. locationKey: " + getLocationKey(dhtSignatureKeyPair.getPublic()));
|
||||
FutureRemove futureRemove = removeAllDataFromMapOfMyProtectedDomain(getLocationKey(dhtSignatureKeyPair.getPublic()));
|
||||
futureRemove.addListener(new BaseFutureListener<BaseFuture>() {
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception {
|
||||
|
@ -17,8 +17,12 @@
|
||||
|
||||
package io.bitsquare.p2p.tomp2p;
|
||||
|
||||
import io.bitsquare.crypto.EncryptionService;
|
||||
import io.bitsquare.p2p.EncryptedMailboxMessage;
|
||||
import io.bitsquare.crypto.CryptoException;
|
||||
import io.bitsquare.crypto.CryptoService;
|
||||
import io.bitsquare.crypto.MessageWithPubKey;
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.crypto.SealedAndSignedMessage;
|
||||
import io.bitsquare.p2p.DecryptedMessageHandler;
|
||||
import io.bitsquare.p2p.MailboxMessage;
|
||||
import io.bitsquare.p2p.MailboxService;
|
||||
import io.bitsquare.p2p.Message;
|
||||
@ -26,10 +30,7 @@ import io.bitsquare.p2p.MessageHandler;
|
||||
import io.bitsquare.p2p.MessageService;
|
||||
import io.bitsquare.p2p.Peer;
|
||||
import io.bitsquare.p2p.listener.SendMessageListener;
|
||||
|
||||
import org.bitcoinj.core.ECKey;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
@ -44,10 +45,12 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TomP2PMessageService extends TomP2PService implements MessageService {
|
||||
private static final Logger log = LoggerFactory.getLogger(TomP2PMessageService.class);
|
||||
private static final int MAX_MESSAGE_SIZE = 100 * 1024; // 34 kb is currently the max size used
|
||||
|
||||
private final CopyOnWriteArrayList<MessageHandler> messageHandlers = new CopyOnWriteArrayList<>();
|
||||
private final CopyOnWriteArrayList<DecryptedMessageHandler> decryptedMessageHandlers = new CopyOnWriteArrayList<>();
|
||||
private final MailboxService mailboxService;
|
||||
private final EncryptionService<MailboxMessage> encryptionService;
|
||||
private final CryptoService<MailboxMessage> cryptoService;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -55,10 +58,10 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public TomP2PMessageService(TomP2PNode tomP2PNode, MailboxService mailboxService, EncryptionService<MailboxMessage> encryptionService) {
|
||||
public TomP2PMessageService(TomP2PNode tomP2PNode, MailboxService mailboxService, CryptoService<MailboxMessage> cryptoService) {
|
||||
super(tomP2PNode);
|
||||
this.mailboxService = mailboxService;
|
||||
this.encryptionService = encryptionService;
|
||||
this.cryptoService = cryptoService;
|
||||
}
|
||||
|
||||
|
||||
@ -75,73 +78,73 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic
|
||||
|
||||
@Override
|
||||
public void sendMessage(Peer peer, Message message, SendMessageListener listener) {
|
||||
sendMessage(peer, message, null, null, null, listener);
|
||||
doSendMessage(peer, null, message, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Peer peer, Message message, PublicKey recipientP2pSigPubKey, PublicKey recipientP2pEncryptPubKey, ECKey registrationKeyPair,
|
||||
SendMessageListener listener) {
|
||||
|
||||
if (peer == null)
|
||||
throw new IllegalArgumentException("Peer must not be null");
|
||||
else if (!(peer instanceof TomP2PPeer))
|
||||
throw new IllegalArgumentException("Peer must be of type TomP2PPeer");
|
||||
|
||||
FutureDirect futureDirect = peerDHT.peer().sendDirect(((TomP2PPeer) peer).getPeerAddress()).object(message).start();
|
||||
futureDirect.addListener(new BaseFutureListener<BaseFuture>() {
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
log.debug("sendMessage completed");
|
||||
executor.execute(listener::handleResult);
|
||||
}
|
||||
else {
|
||||
if (recipientP2pSigPubKey != null && recipientP2pEncryptPubKey != null) {
|
||||
log.info("sendMessage failed. We will try to send the message to the mailbox. Fault reason: " + futureDirect.failedReason());
|
||||
sendMailboxMessage(recipientP2pSigPubKey, recipientP2pEncryptPubKey, registrationKeyPair, (MailboxMessage) message, listener);
|
||||
}
|
||||
else {
|
||||
log.error("sendMessage failed with reason " + futureDirect.failedReason());
|
||||
executor.execute(listener::handleFault);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception {
|
||||
if (recipientP2pSigPubKey != null && recipientP2pEncryptPubKey != null) {
|
||||
log.info("sendMessage failed with exception. We will try to send the message to the mailbox. Exception: " + t.getMessage());
|
||||
sendMailboxMessage(recipientP2pSigPubKey, recipientP2pEncryptPubKey, registrationKeyPair, (MailboxMessage) message, listener);
|
||||
}
|
||||
else {
|
||||
log.error("sendMessage failed with exception " + t.getMessage());
|
||||
executor.execute(listener::handleFault);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendMailboxMessage(PublicKey recipientP2pSigPubKey, PublicKey recipientP2pEncryptPubKey, ECKey registrationKeyPair, MailboxMessage message,
|
||||
SendMessageListener
|
||||
listener) {
|
||||
byte[] result = null;
|
||||
log.info("sendMailboxMessage called");
|
||||
public void sendEncryptedMessage(Peer peer, PubKeyRing pubKeyRing, Message message, SendMessageListener listener) {
|
||||
assert pubKeyRing != null;
|
||||
try {
|
||||
result = encryptionService.encryptMessage(recipientP2pEncryptPubKey, registrationKeyPair, message);
|
||||
doSendMessage(peer, pubKeyRing, cryptoService.encryptAndSignMessage(pubKeyRing, message), listener);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
log.error(t.getMessage());
|
||||
executor.execute(listener::handleFault);
|
||||
}
|
||||
EncryptedMailboxMessage encrypted = new EncryptedMailboxMessage(result);
|
||||
mailboxService.addMessage(recipientP2pSigPubKey,
|
||||
encrypted,
|
||||
}
|
||||
|
||||
private void doSendMessage(Peer peer, PubKeyRing pubKeyRing, Message message, SendMessageListener listener) {
|
||||
log.debug("sendMessage called");
|
||||
if (peer == null)
|
||||
throw new IllegalArgumentException("Peer must not be null");
|
||||
else if (!(peer instanceof TomP2PPeer))
|
||||
throw new IllegalArgumentException("Peer must be of type TomP2PPeer");
|
||||
|
||||
try {
|
||||
FutureDirect futureDirect = peerDHT.peer().sendDirect(((TomP2PPeer) peer).getPeerAddress()).object(message).start();
|
||||
futureDirect.addListener(new BaseFutureListener<BaseFuture>() {
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
log.debug("sendMessage completed");
|
||||
executor.execute(listener::handleResult);
|
||||
}
|
||||
else {
|
||||
log.info("sendMessage failed. We will try to send the message to the mailbox. Fault reason: " +
|
||||
futureDirect.failedReason());
|
||||
if (pubKeyRing != null)
|
||||
sendMailboxMessage(pubKeyRing, (SealedAndSignedMessage) message, listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception {
|
||||
log.info("sendMessage failed with exception. We will try to send the message to the mailbox. Exception: "
|
||||
+ t.getMessage());
|
||||
if (pubKeyRing != null)
|
||||
sendMailboxMessage(pubKeyRing, (SealedAndSignedMessage) message, listener);
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
log.error(t.getMessage());
|
||||
executor.execute(listener::handleFault);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void sendMailboxMessage(PubKeyRing pubKeyRing, SealedAndSignedMessage message, SendMessageListener listener) {
|
||||
log.info("sendMailboxMessage called");
|
||||
mailboxService.addMessage(
|
||||
pubKeyRing,
|
||||
message,
|
||||
() -> {
|
||||
log.debug("Message successfully added to peers mailbox.");
|
||||
executor.execute(listener::handleResult);
|
||||
},
|
||||
(errorMessage, throwable) -> {
|
||||
log.error("Message failed to add to peers mailbox.");
|
||||
log.error("Message failed to add to peers mailbox.");
|
||||
executor.execute(listener::handleFault);
|
||||
}
|
||||
);
|
||||
@ -159,6 +162,18 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic
|
||||
log.error("Try to remove listener which was never added.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDecryptedMessageHandler(DecryptedMessageHandler listener) {
|
||||
if (!decryptedMessageHandlers.add(listener))
|
||||
log.error("Add listener did not change list. Probably listener has been already added.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDecryptedMessageHandler(DecryptedMessageHandler listener) {
|
||||
if (!decryptedMessageHandlers.remove(listener))
|
||||
log.error("Try to remove listener which was never added.");
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
@ -166,21 +181,47 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic
|
||||
|
||||
private void setupReplyHandler() {
|
||||
peerDHT.peer().objectDataReply((sender, message) -> {
|
||||
log.debug("handleMessage peerAddress " + sender);
|
||||
log.debug("handleMessage message " + message);
|
||||
log.debug("Incoming message with peerAddress " + sender);
|
||||
log.debug("Incoming message with type " + message);
|
||||
|
||||
int messageSize = 0;
|
||||
if (message != null)
|
||||
messageSize = Utilities.objectToBytArray(message).length;
|
||||
|
||||
log.debug("Incoming message with size " + messageSize);
|
||||
|
||||
if (!sender.equals(peerDHT.peer().peerAddress())) {
|
||||
if (message instanceof Message)
|
||||
if (messageSize == 0)
|
||||
log.warn("Received msg is null");
|
||||
else if (messageSize > MAX_MESSAGE_SIZE)
|
||||
log.warn("Received msg size of {} is exceeding the max message size of {}.",
|
||||
Utilities.objectToBytArray(message).length, MAX_MESSAGE_SIZE);
|
||||
else if (message instanceof SealedAndSignedMessage)
|
||||
executor.execute(() -> decryptedMessageHandlers.stream().forEach(e -> {
|
||||
MessageWithPubKey messageWithPubKey = null;
|
||||
try {
|
||||
messageWithPubKey = getDecryptedMessageWithPubKey((SealedAndSignedMessage) message);
|
||||
log.debug("decrypted message " + messageWithPubKey.getMessage());
|
||||
e.handleMessage(messageWithPubKey, new TomP2PPeer(sender));
|
||||
} catch (CryptoException e1) {
|
||||
e1.printStackTrace();
|
||||
log.warn("decryptAndVerifyMessage msg failed", e1.getMessage());
|
||||
}
|
||||
}
|
||||
));
|
||||
else if (message instanceof Message)
|
||||
executor.execute(() -> messageHandlers.stream().forEach(e -> e.handleMessage((Message) message, new TomP2PPeer(sender))));
|
||||
else
|
||||
log.error("We got an object which is not type of Message. That must never happen. Request object = " + message);
|
||||
log.warn("We got an object which is not type of Message. Object = " + message);
|
||||
}
|
||||
else {
|
||||
log.error("Received msg from myself. That must never happen.");
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private MessageWithPubKey getDecryptedMessageWithPubKey(SealedAndSignedMessage message) throws CryptoException {
|
||||
return cryptoService.decryptAndVerifyMessage((SealedAndSignedMessage) message);
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
package io.bitsquare.trade;
|
||||
|
||||
import io.bitsquare.crypto.Util;
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.offer.Offer;
|
||||
|
||||
@ -25,8 +25,6 @@ import org.bitcoinj.core.Coin;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@ -52,8 +50,8 @@ public class Contract implements Serializable {
|
||||
String sellerAccountID,
|
||||
FiatAccount buyerFiatAccount,
|
||||
FiatAccount sellerFiatAccount,
|
||||
PublicKey buyerP2pSigPubKey,
|
||||
PublicKey sellerP2pSigPubKey) {
|
||||
PubKeyRing buyerPubKeyRing,
|
||||
PubKeyRing sellerPubKeyRing) {
|
||||
this.offer = offer;
|
||||
this.tradeAmount = tradeAmount;
|
||||
this.takeOfferFeeTxID = takeOfferFeeTxID;
|
||||
@ -61,8 +59,8 @@ public class Contract implements Serializable {
|
||||
this.sellerAccountID = sellerAccountID;
|
||||
this.buyerFiatAccount = buyerFiatAccount;
|
||||
this.sellerFiatAccount = sellerFiatAccount;
|
||||
this.buyerP2pSigPubKeyAsString = Util.getHexFromPubKey(buyerP2pSigPubKey);
|
||||
this.sellerP2pSigPubKeyAsString = Util.getHexFromPubKey(sellerP2pSigPubKey);
|
||||
this.buyerP2pSigPubKeyAsString = buyerPubKeyRing.toString();
|
||||
this.sellerP2pSigPubKeyAsString = sellerPubKeyRing.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -22,10 +22,11 @@ import io.bitsquare.btc.BlockChainService;
|
||||
import io.bitsquare.btc.TradeWalletService;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.common.taskrunner.Model;
|
||||
import io.bitsquare.crypto.SignatureService;
|
||||
import io.bitsquare.crypto.CryptoService;
|
||||
import io.bitsquare.crypto.KeyRing;
|
||||
import io.bitsquare.crypto.MessageWithPubKey;
|
||||
import io.bitsquare.offer.Offer;
|
||||
import io.bitsquare.p2p.AddressService;
|
||||
import io.bitsquare.p2p.MailboxMessage;
|
||||
import io.bitsquare.p2p.MessageService;
|
||||
import io.bitsquare.p2p.Peer;
|
||||
import io.bitsquare.storage.Storage;
|
||||
@ -95,10 +96,10 @@ abstract public class Trade implements Model, Serializable {
|
||||
private final Date creationDate;
|
||||
|
||||
// Mutable
|
||||
private MessageWithPubKey messageWithPubKey;
|
||||
private Date takeOfferDate;
|
||||
protected TradeState.ProcessState processState;
|
||||
protected TradeState.LifeCycleState lifeCycleState;
|
||||
private MailboxMessage mailboxMessage;
|
||||
private Transaction depositTx;
|
||||
private Contract contract;
|
||||
private String contractAsJson;
|
||||
@ -156,9 +157,10 @@ abstract public class Trade implements Model, Serializable {
|
||||
AddressService addressService,
|
||||
TradeWalletService tradeWalletService,
|
||||
BlockChainService blockChainService,
|
||||
SignatureService signatureService,
|
||||
CryptoService cryptoService,
|
||||
ArbitrationRepository arbitrationRepository,
|
||||
User user) {
|
||||
User user,
|
||||
KeyRing keyRing) {
|
||||
|
||||
processModel.onAllServicesInitialized(offer,
|
||||
messageService,
|
||||
@ -166,18 +168,19 @@ abstract public class Trade implements Model, Serializable {
|
||||
walletService,
|
||||
tradeWalletService,
|
||||
blockChainService,
|
||||
signatureService,
|
||||
cryptoService,
|
||||
arbitrationRepository,
|
||||
user);
|
||||
user,
|
||||
keyRing);
|
||||
|
||||
createProtocol();
|
||||
|
||||
tradeProtocol.checkPayoutTxTimeLock(this);
|
||||
|
||||
if (mailboxMessage != null) {
|
||||
tradeProtocol.applyMailboxMessage(mailboxMessage, this);
|
||||
if (messageWithPubKey != null) {
|
||||
tradeProtocol.applyMailboxMessage(messageWithPubKey, this);
|
||||
// After applied to protocol we remove it
|
||||
mailboxMessage = null;
|
||||
messageWithPubKey = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,8 +223,8 @@ abstract public class Trade implements Model, Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
public void setMailboxMessage(MailboxMessage mailboxMessage) {
|
||||
this.mailboxMessage = mailboxMessage;
|
||||
public void setMailboxMessage(MessageWithPubKey messageWithPubKey) {
|
||||
this.messageWithPubKey = messageWithPubKey;
|
||||
}
|
||||
|
||||
public void setStorage(Storage<? extends TradeList> storage) {
|
||||
@ -463,7 +466,7 @@ abstract public class Trade implements Model, Serializable {
|
||||
", processModel=" + processModel +
|
||||
", processState=" + processState +
|
||||
", lifeCycleState=" + lifeCycleState +
|
||||
", mailboxMessage=" + mailboxMessage +
|
||||
", messageWithPubKey=" + messageWithPubKey +
|
||||
", depositTx=" + depositTx +
|
||||
/* ", contract=" + contract +
|
||||
", contractAsJson='" + contractAsJson + '\'' +*/
|
||||
|
@ -25,15 +25,17 @@ import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.FaultHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.crypto.EncryptionService;
|
||||
import io.bitsquare.crypto.SignatureService;
|
||||
import io.bitsquare.crypto.CryptoService;
|
||||
import io.bitsquare.crypto.KeyRing;
|
||||
import io.bitsquare.crypto.MessageWithPubKey;
|
||||
import io.bitsquare.crypto.SealedAndSignedMessage;
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.offer.Offer;
|
||||
import io.bitsquare.offer.OfferBookService;
|
||||
import io.bitsquare.p2p.AddressService;
|
||||
import io.bitsquare.p2p.EncryptedMailboxMessage;
|
||||
import io.bitsquare.p2p.MailboxMessage;
|
||||
import io.bitsquare.p2p.MailboxService;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.p2p.MessageService;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.trade.handlers.TakeOfferResultHandler;
|
||||
@ -79,6 +81,7 @@ public class TradeManager {
|
||||
private static final Logger log = LoggerFactory.getLogger(TradeManager.class);
|
||||
|
||||
private final User user;
|
||||
private KeyRing keyRing;
|
||||
private final AccountSettings accountSettings;
|
||||
private final MessageService messageService;
|
||||
private final MailboxService mailboxService;
|
||||
@ -86,8 +89,7 @@ public class TradeManager {
|
||||
private final BlockChainService blockChainService;
|
||||
private final WalletService walletService;
|
||||
private final TradeWalletService tradeWalletService;
|
||||
private final SignatureService signatureService;
|
||||
private final EncryptionService<MailboxMessage> encryptionService;
|
||||
private final CryptoService<MailboxMessage> cryptoService;
|
||||
private final OfferBookService offerBookService;
|
||||
private final ArbitrationRepository arbitrationRepository;
|
||||
|
||||
@ -107,6 +109,7 @@ public class TradeManager {
|
||||
|
||||
@Inject
|
||||
public TradeManager(User user,
|
||||
KeyRing keyRing,
|
||||
AccountSettings accountSettings,
|
||||
MessageService messageService,
|
||||
MailboxService mailboxService,
|
||||
@ -114,12 +117,12 @@ public class TradeManager {
|
||||
BlockChainService blockChainService,
|
||||
WalletService walletService,
|
||||
TradeWalletService tradeWalletService,
|
||||
SignatureService signatureService,
|
||||
EncryptionService<MailboxMessage> encryptionService,
|
||||
CryptoService<MailboxMessage> cryptoService,
|
||||
OfferBookService offerBookService,
|
||||
ArbitrationRepository arbitrationRepository,
|
||||
@Named("storage.dir") File storageDir) {
|
||||
this.user = user;
|
||||
this.keyRing = keyRing;
|
||||
this.accountSettings = accountSettings;
|
||||
this.messageService = messageService;
|
||||
this.mailboxService = mailboxService;
|
||||
@ -127,8 +130,7 @@ public class TradeManager {
|
||||
this.blockChainService = blockChainService;
|
||||
this.walletService = walletService;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
this.signatureService = signatureService;
|
||||
this.encryptionService = encryptionService;
|
||||
this.cryptoService = cryptoService;
|
||||
this.offerBookService = offerBookService;
|
||||
this.arbitrationRepository = arbitrationRepository;
|
||||
|
||||
@ -167,25 +169,27 @@ public class TradeManager {
|
||||
|
||||
// If there are messages in our mailbox we apply it and remove them from the DHT
|
||||
// We run that before initializing the pending trades to be sure the state is correct
|
||||
mailboxService.getAllMessages(user.getP2pSigPubKey(),
|
||||
mailboxService.getAllMessages(
|
||||
(encryptedMailboxMessages) -> {
|
||||
log.trace("mailboxService.getAllMessages success");
|
||||
setMailboxMessagesToTrades(encryptedMailboxMessages);
|
||||
emptyMailbox();
|
||||
//TODO testing
|
||||
//emptyMailbox();
|
||||
initPendingTrades();
|
||||
});
|
||||
}
|
||||
|
||||
private void setMailboxMessagesToTrades(List<EncryptedMailboxMessage> encryptedMailboxMessages) {
|
||||
log.trace("applyMailboxMessage encryptedMailboxMessage.size=" + encryptedMailboxMessages.size());
|
||||
for (EncryptedMailboxMessage encrypted : encryptedMailboxMessages) {
|
||||
private void setMailboxMessagesToTrades(List<SealedAndSignedMessage> encryptedMessages) {
|
||||
log.trace("applyMailboxMessage encryptedMailboxMessage.size=" + encryptedMessages.size());
|
||||
for (SealedAndSignedMessage encrypted : encryptedMessages) {
|
||||
try {
|
||||
MailboxMessage mailboxMessage = encryptionService.decryptToMessage(user.getP2pEncryptPrivateKey(), encrypted.getBytes());
|
||||
if (mailboxMessage instanceof TradeMessage) {
|
||||
String tradeId = ((TradeMessage) mailboxMessage).tradeId;
|
||||
MessageWithPubKey messageWithPubKey = cryptoService.decryptAndVerifyMessage(encrypted);
|
||||
Message message = messageWithPubKey.getMessage();
|
||||
if (message instanceof MailboxMessage && message instanceof TradeMessage) {
|
||||
String tradeId = ((TradeMessage) message).tradeId;
|
||||
Optional<Trade> tradeOptional = pendingTrades.stream().filter(e -> e.getId().equals(tradeId)).findAny();
|
||||
if (tradeOptional.isPresent())
|
||||
tradeOptional.get().setMailboxMessage(mailboxMessage);
|
||||
tradeOptional.get().setMailboxMessage(messageWithPubKey);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
@ -195,7 +199,7 @@ public class TradeManager {
|
||||
}
|
||||
|
||||
private void emptyMailbox() {
|
||||
mailboxService.removeAllMessages(user.getP2pSigPubKey(),
|
||||
mailboxService.removeAllMessages(
|
||||
() -> log.debug("All mailbox entries removed"),
|
||||
(errorMessage, fault) -> {
|
||||
log.error(errorMessage);
|
||||
@ -259,7 +263,7 @@ public class TradeManager {
|
||||
|
||||
FiatAccount fiatAccount = user.currentFiatAccountProperty().get();
|
||||
Offer offer = new Offer(id,
|
||||
user.getP2pSigPubKey(),
|
||||
keyRing.getPubKeyRing(),
|
||||
direction,
|
||||
price.getValue(),
|
||||
amount,
|
||||
@ -352,6 +356,7 @@ public class TradeManager {
|
||||
if (!checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) {
|
||||
CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel(
|
||||
offer,
|
||||
keyRing.getPubKeyRing(),
|
||||
messageService,
|
||||
addressService);
|
||||
|
||||
@ -373,15 +378,15 @@ public class TradeManager {
|
||||
|
||||
// First we check if offer is still available then we create the trade with the protocol
|
||||
public void requestTakeOffer(Coin amount, Offer offer, TakeOfferResultHandler takeOfferResultHandler) {
|
||||
CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel(offer, messageService, addressService);
|
||||
CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel(offer, keyRing.getPubKeyRing(), messageService, addressService);
|
||||
CheckOfferAvailabilityProtocol availabilityProtocol = new CheckOfferAvailabilityProtocol(model,
|
||||
() -> handleCheckOfferAvailabilityResult(amount, offer, model, takeOfferResultHandler),
|
||||
() -> createTrade(amount, offer, model, takeOfferResultHandler),
|
||||
(errorMessage) -> disposeCheckOfferAvailabilityRequest(offer));
|
||||
checkOfferAvailabilityProtocolMap.put(offer.getId(), availabilityProtocol);
|
||||
availabilityProtocol.checkOfferAvailability();
|
||||
}
|
||||
|
||||
private void handleCheckOfferAvailabilityResult(Coin amount, Offer offer, CheckOfferAvailabilityModel model, TakeOfferResultHandler
|
||||
private void createTrade(Coin amount, Offer offer, CheckOfferAvailabilityModel model, TakeOfferResultHandler
|
||||
takeOfferResultHandler) {
|
||||
disposeCheckOfferAvailabilityRequest(offer);
|
||||
if (offer.getState() == Offer.State.AVAILABLE) {
|
||||
@ -487,9 +492,13 @@ public class TradeManager {
|
||||
addressService,
|
||||
tradeWalletService,
|
||||
blockChainService,
|
||||
signatureService,
|
||||
cryptoService,
|
||||
arbitrationRepository,
|
||||
user);
|
||||
user,
|
||||
keyRing);
|
||||
}
|
||||
|
||||
public boolean isMyOffer(Offer offer) {
|
||||
return offer.getPubKeyRing().getHashString().equals(keyRing.getPubKeyRing().getHashString());
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
package io.bitsquare.trade.protocol.availability;
|
||||
|
||||
import io.bitsquare.common.taskrunner.Model;
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.offer.Offer;
|
||||
import io.bitsquare.p2p.AddressService;
|
||||
import io.bitsquare.p2p.MessageService;
|
||||
@ -31,14 +32,16 @@ public class CheckOfferAvailabilityModel implements Model {
|
||||
private static final Logger log = LoggerFactory.getLogger(CheckOfferAvailabilityModel.class);
|
||||
|
||||
public final Offer offer;
|
||||
private final PubKeyRing pubKeyRing;
|
||||
public final MessageService messageService;
|
||||
public final AddressService addressService;
|
||||
|
||||
private Peer peer;
|
||||
private OfferMessage message;
|
||||
|
||||
public CheckOfferAvailabilityModel(Offer offer, MessageService messageService, AddressService addressService) {
|
||||
public CheckOfferAvailabilityModel(Offer offer, PubKeyRing pubKeyRing, MessageService messageService, AddressService addressService) {
|
||||
this.offer = offer;
|
||||
this.pubKeyRing = pubKeyRing;
|
||||
this.messageService = messageService;
|
||||
this.addressService = addressService;
|
||||
}
|
||||
@ -68,4 +71,8 @@ public class CheckOfferAvailabilityModel implements Model {
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
|
||||
public PubKeyRing getPubKeyRing() {
|
||||
return pubKeyRing;
|
||||
}
|
||||
}
|
||||
|
@ -20,14 +20,16 @@ package io.bitsquare.trade.protocol.availability;
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.common.taskrunner.TaskRunner;
|
||||
import io.bitsquare.crypto.MessageWithPubKey;
|
||||
import io.bitsquare.offer.Offer;
|
||||
import io.bitsquare.p2p.DecryptedMessageHandler;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.p2p.MessageHandler;
|
||||
import io.bitsquare.p2p.Peer;
|
||||
import io.bitsquare.trade.protocol.availability.messages.OfferMessage;
|
||||
import io.bitsquare.trade.protocol.availability.messages.ReportOfferAvailabilityMessage;
|
||||
import io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress;
|
||||
import io.bitsquare.trade.protocol.availability.tasks.ProcessReportOfferAvailabilityMessage;
|
||||
import io.bitsquare.trade.protocol.availability.tasks.RequestIsOfferAvailable;
|
||||
import io.bitsquare.trade.protocol.availability.tasks.SendRequestIsOfferAvailableMessage;
|
||||
|
||||
import org.bitcoinj.utils.Threading;
|
||||
|
||||
@ -37,6 +39,8 @@ import java.util.TimerTask;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static io.bitsquare.util.Validator.nonEmptyStringOf;
|
||||
|
||||
public class CheckOfferAvailabilityProtocol {
|
||||
private static final Logger log = LoggerFactory.getLogger(CheckOfferAvailabilityProtocol.class);
|
||||
|
||||
@ -45,7 +49,7 @@ public class CheckOfferAvailabilityProtocol {
|
||||
private final CheckOfferAvailabilityModel model;
|
||||
private final ResultHandler resultHandler;
|
||||
private final ErrorMessageHandler errorMessageHandler;
|
||||
private final MessageHandler messageHandler;
|
||||
private final DecryptedMessageHandler decryptedMessageHandler;
|
||||
private Timer timeoutTimer;
|
||||
|
||||
private boolean isCanceled;
|
||||
@ -60,12 +64,12 @@ public class CheckOfferAvailabilityProtocol {
|
||||
this.model = model;
|
||||
this.resultHandler = resultHandler;
|
||||
this.errorMessageHandler = errorMessageHandler;
|
||||
messageHandler = this::handleMessage;
|
||||
decryptedMessageHandler = this::handleDecryptedMessageWithPubKey;
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
stopTimeout();
|
||||
model.messageService.removeMessageHandler(messageHandler);
|
||||
model.messageService.removeDecryptedMessageHandler(decryptedMessageHandler);
|
||||
}
|
||||
|
||||
|
||||
@ -77,7 +81,7 @@ public class CheckOfferAvailabilityProtocol {
|
||||
// reset
|
||||
model.offer.setState(Offer.State.UNKNOWN);
|
||||
|
||||
model.messageService.addMessageHandler(messageHandler);
|
||||
model.messageService.addDecryptedMessageHandler(decryptedMessageHandler);
|
||||
|
||||
taskRunner = new TaskRunner<>(model,
|
||||
() -> log.debug("sequence at onCheckOfferAvailability completed"),
|
||||
@ -85,7 +89,7 @@ public class CheckOfferAvailabilityProtocol {
|
||||
);
|
||||
taskRunner.addTasks(
|
||||
GetPeerAddress.class,
|
||||
RequestIsOfferAvailable.class
|
||||
SendRequestIsOfferAvailableMessage.class
|
||||
);
|
||||
startTimeout();
|
||||
taskRunner.run();
|
||||
@ -102,14 +106,18 @@ public class CheckOfferAvailabilityProtocol {
|
||||
// Incoming message handling
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void handleMessage(Message message, @SuppressWarnings("UnusedParameters") Peer sender) {
|
||||
if (!isCanceled) {
|
||||
if (message instanceof ReportOfferAvailabilityMessage && model.offer.getId().equals(((ReportOfferAvailabilityMessage) message).offerId))
|
||||
handle((ReportOfferAvailabilityMessage) message);
|
||||
protected void handleDecryptedMessageWithPubKey(MessageWithPubKey messageWithPubKey, Peer sender) {
|
||||
Message message = messageWithPubKey.getMessage();
|
||||
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender);
|
||||
if (message instanceof OfferMessage) {
|
||||
nonEmptyStringOf(((OfferMessage) message).offerId);
|
||||
if (message instanceof ReportOfferAvailabilityMessage && model.offer.getId().equals(((OfferMessage) message).offerId))
|
||||
handleDecryptedMessage((ReportOfferAvailabilityMessage) message);
|
||||
}
|
||||
}
|
||||
|
||||
private void handle(ReportOfferAvailabilityMessage message) {
|
||||
|
||||
private void handleDecryptedMessage(ReportOfferAvailabilityMessage message) {
|
||||
stopTimeout();
|
||||
model.setMessage(message);
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
package io.bitsquare.trade.protocol.availability.messages;
|
||||
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
||||
|
||||
import java.io.Serializable;
|
||||
@ -25,7 +26,14 @@ public class RequestIsOfferAvailableMessage extends TradeMessage implements Seri
|
||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public RequestIsOfferAvailableMessage(String tradeId) {
|
||||
private final PubKeyRing pubKeyRing;
|
||||
|
||||
public RequestIsOfferAvailableMessage(String tradeId, PubKeyRing pubKeyRing) {
|
||||
super(tradeId);
|
||||
this.pubKeyRing = pubKeyRing;
|
||||
}
|
||||
|
||||
public PubKeyRing getPubKeyRing() {
|
||||
return pubKeyRing;
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ public class GetPeerAddress extends Task<CheckOfferAvailabilityModel> {
|
||||
@Override
|
||||
protected void doRun() {
|
||||
try {
|
||||
model.addressService.findPeerAddress(model.offer.getP2pSigPubKey(), new GetPeerAddressListener() {
|
||||
model.addressService.findPeerAddress(model.offer.getPubKeyRing(), new GetPeerAddressListener() {
|
||||
@Override
|
||||
public void onResult(Peer peer) {
|
||||
model.setPeer(peer);
|
||||
|
@ -27,17 +27,21 @@ import io.bitsquare.trade.protocol.availability.messages.RequestIsOfferAvailable
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class RequestIsOfferAvailable extends Task<CheckOfferAvailabilityModel> {
|
||||
private static final Logger log = LoggerFactory.getLogger(RequestIsOfferAvailable.class);
|
||||
public class SendRequestIsOfferAvailableMessage extends Task<CheckOfferAvailabilityModel> {
|
||||
private static final Logger log = LoggerFactory.getLogger(SendRequestIsOfferAvailableMessage.class);
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public RequestIsOfferAvailable(TaskRunner taskHandler, CheckOfferAvailabilityModel model) {
|
||||
public SendRequestIsOfferAvailableMessage(TaskRunner taskHandler, CheckOfferAvailabilityModel model) {
|
||||
super(taskHandler, model);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
try {
|
||||
model.messageService.sendMessage(model.getPeer(), new RequestIsOfferAvailableMessage(model.offer.getId()),
|
||||
RequestIsOfferAvailableMessage message = new RequestIsOfferAvailableMessage(model.offer.getId(), model.getPubKeyRing());
|
||||
model.messageService.sendEncryptedMessage(model.getPeer(),
|
||||
model.offer.getPubKeyRing(),
|
||||
message,
|
||||
new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
@ -17,7 +17,6 @@
|
||||
|
||||
package io.bitsquare.trade.protocol.trade;
|
||||
|
||||
import io.bitsquare.p2p.MailboxMessage;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.p2p.Peer;
|
||||
import io.bitsquare.p2p.listener.SendMessageListener;
|
||||
@ -49,24 +48,23 @@ import io.bitsquare.trade.states.OffererTradeState;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static io.bitsquare.util.Validator.*;
|
||||
import static io.bitsquare.util.Validator.checkTradeId;
|
||||
|
||||
public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtocol, OffererProtocol {
|
||||
private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererProtocol.class);
|
||||
|
||||
private final BuyerAsOffererTrade buyerAsOffererTrade;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public BuyerAsOffererProtocol(BuyerAsOffererTrade trade) {
|
||||
super(trade.getProcessModel());
|
||||
|
||||
log.debug("New OffererProtocol " + this);
|
||||
this.buyerAsOffererTrade = trade;
|
||||
messageHandler = this::handleMessage;
|
||||
|
||||
processModel.getMessageService().addMessageHandler(messageHandler);
|
||||
}
|
||||
|
||||
|
||||
@ -75,16 +73,14 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) {
|
||||
public void doApplyMailboxMessage(Message message, Trade trade) {
|
||||
this.trade = trade;
|
||||
|
||||
log.debug("setMailboxMessage " + mailboxMessage);
|
||||
|
||||
// Find first the actual peer address, as it might have changed in the meantime
|
||||
findPeerAddress(processModel.tradingPeer.getP2pSigPubKey(),
|
||||
findPeerAddress(processModel.tradingPeer.getPubKeyRing(),
|
||||
() -> {
|
||||
if (mailboxMessage instanceof RequestFinalizePayoutTxMessage) {
|
||||
handle((RequestFinalizePayoutTxMessage) mailboxMessage);
|
||||
if (message instanceof RequestFinalizePayoutTxMessage) {
|
||||
handle((RequestFinalizePayoutTxMessage) message);
|
||||
}
|
||||
},
|
||||
(errorMessage -> {
|
||||
@ -98,28 +94,33 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// OpenOffer requests
|
||||
private void handle(RequestIsOfferAvailableMessage tradeMessage, Peer sender) {
|
||||
// We get an encrypted message but don't do the signature check as we don't know the peer yet.
|
||||
// A basic sig check is in done also at decryption time
|
||||
private void handle(RequestIsOfferAvailableMessage message, Peer sender) {
|
||||
try {
|
||||
checkTradeId(processModel.getId(), tradeMessage);
|
||||
checkTradeId(processModel.getId(), message);
|
||||
|
||||
// We don't store anything in the offererTradeProcessModel as we might be in a trade process and receive that request from another peer who wants
|
||||
// to take the
|
||||
// offer
|
||||
// at the same time
|
||||
// to take the offer at the same time
|
||||
boolean isOfferOpen = buyerAsOffererTrade.lifeCycleStateProperty().get() == OffererTradeState.LifeCycleState.OFFER_OPEN;
|
||||
ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(processModel.getId(), isOfferOpen);
|
||||
processModel.getMessageService().sendMessage(sender, reportOfferAvailabilityMessage, new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
// Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade.
|
||||
log.trace("ReportOfferAvailabilityMessage successfully arrived at peer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFault() {
|
||||
log.warn("Sending ReportOfferAvailabilityMessage failed.");
|
||||
}
|
||||
});
|
||||
ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(processModel.getId(), isOfferOpen);
|
||||
processModel.getMessageService().sendEncryptedMessage(sender,
|
||||
message.getPubKeyRing(),
|
||||
reportOfferAvailabilityMessage,
|
||||
new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
// Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade.
|
||||
log.trace("ReportOfferAvailabilityMessage successfully arrived at peer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFault() {
|
||||
// We don't handle the error as we might be in a trade process with another trader
|
||||
log.warn("Sending ReportOfferAvailabilityMessage failed.");
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
// We don't handle the error as we might be in a trade process with another trader
|
||||
t.printStackTrace();
|
||||
@ -214,29 +215,22 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
|
||||
// Massage dispatcher
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void handleMessage(Message message, Peer sender) {
|
||||
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender);
|
||||
if (message instanceof TradeMessage) {
|
||||
TradeMessage tradeMessage = (TradeMessage) message;
|
||||
nonEmptyStringOf(tradeMessage.tradeId);
|
||||
|
||||
if (tradeMessage.tradeId.equals(buyerAsOffererTrade.getId())) {
|
||||
if (tradeMessage instanceof RequestIsOfferAvailableMessage) {
|
||||
handle((RequestIsOfferAvailableMessage) tradeMessage, sender);
|
||||
}
|
||||
else if (tradeMessage instanceof RequestDepositTxInputsMessage) {
|
||||
handle((RequestDepositTxInputsMessage) tradeMessage, sender);
|
||||
}
|
||||
else if (tradeMessage instanceof RequestPublishDepositTxMessage) {
|
||||
handle((RequestPublishDepositTxMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof RequestFinalizePayoutTxMessage) {
|
||||
handle((RequestFinalizePayoutTxMessage) tradeMessage);
|
||||
}
|
||||
else {
|
||||
log.error("Incoming tradeMessage not supported. " + tradeMessage);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) {
|
||||
if (tradeMessage instanceof RequestIsOfferAvailableMessage) {
|
||||
handle((RequestIsOfferAvailableMessage) tradeMessage, sender);
|
||||
}
|
||||
else if (tradeMessage instanceof RequestDepositTxInputsMessage) {
|
||||
handle((RequestDepositTxInputsMessage) tradeMessage, sender);
|
||||
}
|
||||
else if (tradeMessage instanceof RequestPublishDepositTxMessage) {
|
||||
handle((RequestPublishDepositTxMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof RequestFinalizePayoutTxMessage) {
|
||||
handle((RequestFinalizePayoutTxMessage) tradeMessage);
|
||||
}
|
||||
else {
|
||||
log.error("Incoming decrypted tradeMessage not supported. " + tradeMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package io.bitsquare.trade.protocol.trade;
|
||||
|
||||
import io.bitsquare.p2p.MailboxMessage;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.p2p.Peer;
|
||||
import io.bitsquare.trade.BuyerAsTakerTrade;
|
||||
@ -46,8 +45,6 @@ import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOffererAccount;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static io.bitsquare.util.Validator.nonEmptyStringOf;
|
||||
|
||||
public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol, TakerProtocol {
|
||||
private static final Logger log = LoggerFactory.getLogger(BuyerAsTakerProtocol.class);
|
||||
|
||||
@ -60,11 +57,11 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
|
||||
|
||||
public BuyerAsTakerProtocol(BuyerAsTakerTrade trade) {
|
||||
super(trade.getProcessModel());
|
||||
|
||||
log.debug("New SellerAsTakerProtocol " + this);
|
||||
this.buyerAsTakerTrade = trade;
|
||||
|
||||
messageHandler = this::handleMessage;
|
||||
processModel.getMessageService().addMessageHandler(messageHandler);
|
||||
processModel.tradingPeer.setPubKeyRing(trade.getOffer().getPubKeyRing());
|
||||
}
|
||||
|
||||
|
||||
@ -73,16 +70,14 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) {
|
||||
public void doApplyMailboxMessage(Message message, Trade trade) {
|
||||
this.trade = trade;
|
||||
|
||||
log.debug("setMailboxMessage " + mailboxMessage);
|
||||
|
||||
// Find first the actual peer address, as it might have changed in the meantime
|
||||
findPeerAddress(trade.getOffer().getP2pSigPubKey(),
|
||||
findPeerAddress(trade.getOffer().getPubKeyRing(),
|
||||
() -> {
|
||||
if (mailboxMessage instanceof RequestFinalizePayoutTxMessage) {
|
||||
handle((RequestFinalizePayoutTxMessage) mailboxMessage);
|
||||
if (message instanceof RequestFinalizePayoutTxMessage) {
|
||||
handle((RequestFinalizePayoutTxMessage) message);
|
||||
}
|
||||
},
|
||||
(errorMessage -> {
|
||||
@ -177,23 +172,16 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
|
||||
// Massage dispatcher
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void handleMessage(Message message, Peer sender) {
|
||||
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender);
|
||||
if (message instanceof TradeMessage) {
|
||||
TradeMessage tradeMessage = (TradeMessage) message;
|
||||
nonEmptyStringOf(tradeMessage.tradeId);
|
||||
|
||||
if (tradeMessage.tradeId.equals(processModel.getId())) {
|
||||
if (tradeMessage instanceof RequestPublishDepositTxMessage) {
|
||||
handle((RequestPublishDepositTxMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof RequestFinalizePayoutTxMessage) {
|
||||
handle((RequestFinalizePayoutTxMessage) tradeMessage);
|
||||
}
|
||||
else {
|
||||
log.error("Incoming message not supported. " + tradeMessage);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) {
|
||||
if (tradeMessage instanceof RequestPublishDepositTxMessage) {
|
||||
handle((RequestPublishDepositTxMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof RequestFinalizePayoutTxMessage) {
|
||||
handle((RequestFinalizePayoutTxMessage) tradeMessage);
|
||||
}
|
||||
else {
|
||||
log.error("Incoming message not supported. " + tradeMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,9 @@ import io.bitsquare.btc.BlockChainService;
|
||||
import io.bitsquare.btc.TradeWalletService;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.common.taskrunner.Model;
|
||||
import io.bitsquare.crypto.SignatureService;
|
||||
import io.bitsquare.crypto.CryptoService;
|
||||
import io.bitsquare.crypto.KeyRing;
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.offer.Offer;
|
||||
import io.bitsquare.p2p.AddressService;
|
||||
@ -39,8 +41,6 @@ import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -60,10 +60,11 @@ public class ProcessModel implements Model, Serializable {
|
||||
transient private WalletService walletService;
|
||||
transient private TradeWalletService tradeWalletService;
|
||||
transient private BlockChainService blockChainService;
|
||||
transient private SignatureService signatureService;
|
||||
transient private CryptoService cryptoService;
|
||||
transient private ArbitrationRepository arbitrationRepository;
|
||||
transient private Offer offer;
|
||||
private transient User user;
|
||||
transient private User user;
|
||||
transient private KeyRing keyRing;
|
||||
|
||||
// Mutable
|
||||
public final TradingPeer tradingPeer;
|
||||
@ -93,18 +94,20 @@ public class ProcessModel implements Model, Serializable {
|
||||
WalletService walletService,
|
||||
TradeWalletService tradeWalletService,
|
||||
BlockChainService blockChainService,
|
||||
SignatureService signatureService,
|
||||
CryptoService cryptoService,
|
||||
ArbitrationRepository arbitrationRepository,
|
||||
User user) {
|
||||
User user,
|
||||
KeyRing keyRing) {
|
||||
this.offer = offer;
|
||||
this.messageService = messageService;
|
||||
this.addressService = addressService;
|
||||
this.walletService = walletService;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
this.blockChainService = blockChainService;
|
||||
this.signatureService = signatureService;
|
||||
this.cryptoService = cryptoService;
|
||||
this.arbitrationRepository = arbitrationRepository;
|
||||
this.user = user;
|
||||
this.keyRing = keyRing;
|
||||
}
|
||||
|
||||
|
||||
@ -128,10 +131,6 @@ public class ProcessModel implements Model, Serializable {
|
||||
return blockChainService;
|
||||
}
|
||||
|
||||
public SignatureService getSignatureService() {
|
||||
return signatureService;
|
||||
}
|
||||
|
||||
public byte[] getArbitratorPubKey() {
|
||||
return arbitrationRepository.getDefaultArbitrator().getPubKey();
|
||||
}
|
||||
@ -179,10 +178,6 @@ public class ProcessModel implements Model, Serializable {
|
||||
return user.getAccountId();
|
||||
}
|
||||
|
||||
public PublicKey getP2pSigPubKey() {
|
||||
return user.getP2pSigPubKey();
|
||||
}
|
||||
|
||||
public byte[] getRegistrationPubKey() {
|
||||
return walletService.getRegistrationAddressEntry().getPubKey();
|
||||
}
|
||||
@ -195,10 +190,6 @@ public class ProcessModel implements Model, Serializable {
|
||||
return getAddressEntry().getPubKey();
|
||||
}
|
||||
|
||||
public PublicKey getP2pEncryptPubKey() {
|
||||
return user.getP2pEncryptPubKey();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<TransactionOutput> getConnectedOutputsForAllInputs() {
|
||||
return connectedOutputsForAllInputs;
|
||||
@ -263,4 +254,16 @@ public class ProcessModel implements Model, Serializable {
|
||||
public AddressService getAddressService() {
|
||||
return addressService;
|
||||
}
|
||||
|
||||
public PubKeyRing getPubKeyRing() {
|
||||
return keyRing.getPubKeyRing();
|
||||
}
|
||||
|
||||
public CryptoService getCryptoService() {
|
||||
return cryptoService;
|
||||
}
|
||||
|
||||
public void setCryptoService(CryptoService cryptoService) {
|
||||
this.cryptoService = cryptoService;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package io.bitsquare.trade.protocol.trade;
|
||||
|
||||
import io.bitsquare.p2p.MailboxMessage;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.p2p.Peer;
|
||||
import io.bitsquare.p2p.listener.SendMessageListener;
|
||||
@ -49,7 +48,7 @@ import io.bitsquare.trade.states.OffererTradeState;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static io.bitsquare.util.Validator.*;
|
||||
import static io.bitsquare.util.Validator.checkTradeId;
|
||||
|
||||
public class SellerAsOffererProtocol extends TradeProtocol implements SellerProtocol, OffererProtocol {
|
||||
private static final Logger log = LoggerFactory.getLogger(SellerAsOffererProtocol.class);
|
||||
@ -63,11 +62,9 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
|
||||
|
||||
public SellerAsOffererProtocol(SellerAsOffererTrade trade) {
|
||||
super(trade.getProcessModel());
|
||||
|
||||
log.debug("New OffererProtocol " + this);
|
||||
this.sellerAsOffererTrade = trade;
|
||||
messageHandler = this::handleMessage;
|
||||
|
||||
processModel.getMessageService().addMessageHandler(messageHandler);
|
||||
}
|
||||
|
||||
|
||||
@ -76,22 +73,21 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) {
|
||||
public void doApplyMailboxMessage(Message message, Trade trade) {
|
||||
this.trade = trade;
|
||||
|
||||
log.debug("setMailboxMessage " + mailboxMessage);
|
||||
// Find first the actual peer address, as it might have changed in the meantime
|
||||
if (mailboxMessage instanceof PayoutTxFinalizedMessage) {
|
||||
handle((PayoutTxFinalizedMessage) mailboxMessage);
|
||||
if (message instanceof PayoutTxFinalizedMessage) {
|
||||
handle((PayoutTxFinalizedMessage) message);
|
||||
}
|
||||
else {
|
||||
findPeerAddress(processModel.tradingPeer.getP2pSigPubKey(),
|
||||
// Find first the actual peer address, as it might have changed in the meantime
|
||||
findPeerAddress(processModel.tradingPeer.getPubKeyRing(),
|
||||
() -> {
|
||||
if (mailboxMessage instanceof FiatTransferStartedMessage) {
|
||||
handle((FiatTransferStartedMessage) mailboxMessage);
|
||||
if (message instanceof FiatTransferStartedMessage) {
|
||||
handle((FiatTransferStartedMessage) message);
|
||||
}
|
||||
else if (mailboxMessage instanceof DepositTxPublishedMessage) {
|
||||
handle((DepositTxPublishedMessage) mailboxMessage);
|
||||
else if (message instanceof DepositTxPublishedMessage) {
|
||||
handle((DepositTxPublishedMessage) message);
|
||||
}
|
||||
},
|
||||
(errorMessage -> {
|
||||
@ -109,29 +105,31 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
|
||||
// IsOfferAvailable
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void handle(RequestIsOfferAvailableMessage tradeMessage, Peer sender) {
|
||||
private void handle(RequestIsOfferAvailableMessage message, Peer sender) {
|
||||
try {
|
||||
checkTradeId(processModel.getId(), tradeMessage);
|
||||
checkTradeId(processModel.getId(), message);
|
||||
|
||||
// We don't store anything in the offererTradeProcessModel as we might be in a trade process and receive that request from another peer who wants
|
||||
// to take the
|
||||
// offer
|
||||
// at the same time
|
||||
// to take the offer at the same time
|
||||
boolean isOfferOpen = sellerAsOffererTrade.lifeCycleStateProperty().get() == OffererTradeState.LifeCycleState.OFFER_OPEN;
|
||||
|
||||
ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(processModel.getId(), isOfferOpen);
|
||||
processModel.getMessageService().sendMessage(sender, reportOfferAvailabilityMessage, new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
// Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade.
|
||||
log.trace("ReportOfferAvailabilityMessage successfully arrived at peer");
|
||||
}
|
||||
processModel.getMessageService().sendEncryptedMessage(sender,
|
||||
message.getPubKeyRing(),
|
||||
reportOfferAvailabilityMessage,
|
||||
new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
// Offerer does not do anything at that moment. Peer might only watch the offer and does not start a trade.
|
||||
log.trace("ReportOfferAvailabilityMessage successfully arrived at peer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFault() {
|
||||
log.warn("Sending ReportOfferAvailabilityMessage failed.");
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void handleFault() {
|
||||
// We don't handle the error as we might be in a trade process with another trader
|
||||
log.warn("Sending ReportOfferAvailabilityMessage failed.");
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
// We don't handle the error as we might be in a trade process with another trader
|
||||
t.printStackTrace();
|
||||
@ -239,31 +237,25 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
|
||||
// Massage dispatcher
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void handleMessage(Message message, Peer sender) {
|
||||
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender);
|
||||
if (message instanceof TradeMessage) {
|
||||
TradeMessage tradeMessage = (TradeMessage) message;
|
||||
nonEmptyStringOf(tradeMessage.tradeId);
|
||||
if (tradeMessage.tradeId.equals(sellerAsOffererTrade.getId())) {
|
||||
if (tradeMessage instanceof RequestIsOfferAvailableMessage) {
|
||||
handle((RequestIsOfferAvailableMessage) tradeMessage, sender);
|
||||
}
|
||||
else if (tradeMessage instanceof RequestPayDepositMessage) {
|
||||
handle((RequestPayDepositMessage) tradeMessage, sender);
|
||||
}
|
||||
else if (tradeMessage instanceof DepositTxPublishedMessage) {
|
||||
handle((DepositTxPublishedMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof FiatTransferStartedMessage) {
|
||||
handle((FiatTransferStartedMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof PayoutTxFinalizedMessage) {
|
||||
handle((PayoutTxFinalizedMessage) tradeMessage);
|
||||
}
|
||||
else {
|
||||
log.error("Incoming tradeMessage not supported. " + tradeMessage);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) {
|
||||
if (tradeMessage instanceof RequestIsOfferAvailableMessage) {
|
||||
handle((RequestIsOfferAvailableMessage) tradeMessage, sender);
|
||||
}
|
||||
else if (tradeMessage instanceof RequestPayDepositMessage) {
|
||||
handle((RequestPayDepositMessage) tradeMessage, sender);
|
||||
}
|
||||
else if (tradeMessage instanceof DepositTxPublishedMessage) {
|
||||
handle((DepositTxPublishedMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof FiatTransferStartedMessage) {
|
||||
handle((FiatTransferStartedMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof PayoutTxFinalizedMessage) {
|
||||
handle((PayoutTxFinalizedMessage) tradeMessage);
|
||||
}
|
||||
else {
|
||||
log.error("Incoming tradeMessage not supported. " + tradeMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package io.bitsquare.trade.protocol.trade;
|
||||
|
||||
import io.bitsquare.p2p.MailboxMessage;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.p2p.Peer;
|
||||
import io.bitsquare.trade.SellerAsTakerTrade;
|
||||
@ -48,8 +47,6 @@ import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOffererAccount;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static io.bitsquare.util.Validator.nonEmptyStringOf;
|
||||
|
||||
public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtocol, TakerProtocol {
|
||||
private static final Logger log = LoggerFactory.getLogger(SellerAsTakerProtocol.class);
|
||||
|
||||
@ -62,11 +59,11 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
|
||||
|
||||
public SellerAsTakerProtocol(SellerAsTakerTrade trade) {
|
||||
super(trade.getProcessModel());
|
||||
|
||||
log.debug("New SellerAsTakerProtocol " + this);
|
||||
this.sellerAsTakerTrade = trade;
|
||||
|
||||
messageHandler = this::handleMessage;
|
||||
processModel.getMessageService().addMessageHandler(messageHandler);
|
||||
processModel.tradingPeer.setPubKeyRing(trade.getOffer().getPubKeyRing());
|
||||
}
|
||||
|
||||
|
||||
@ -75,23 +72,21 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) {
|
||||
public void doApplyMailboxMessage(Message message, Trade trade) {
|
||||
this.trade = trade;
|
||||
|
||||
log.debug("setMailboxMessage " + mailboxMessage);
|
||||
|
||||
// Find first the actual peer address, as it might have changed in the meantime
|
||||
if (mailboxMessage instanceof PayoutTxFinalizedMessage) {
|
||||
handle((PayoutTxFinalizedMessage) mailboxMessage);
|
||||
if (message instanceof PayoutTxFinalizedMessage) {
|
||||
handle((PayoutTxFinalizedMessage) message);
|
||||
}
|
||||
else {
|
||||
findPeerAddress(trade.getOffer().getP2pSigPubKey(),
|
||||
// Find first the actual peer address, as it might have changed in the meantime
|
||||
findPeerAddress(trade.getOffer().getPubKeyRing(),
|
||||
() -> {
|
||||
if (mailboxMessage instanceof FiatTransferStartedMessage) {
|
||||
handle((FiatTransferStartedMessage) mailboxMessage);
|
||||
if (message instanceof FiatTransferStartedMessage) {
|
||||
handle((FiatTransferStartedMessage) message);
|
||||
}
|
||||
else if (mailboxMessage instanceof DepositTxPublishedMessage) {
|
||||
handle((DepositTxPublishedMessage) mailboxMessage);
|
||||
else if (message instanceof DepositTxPublishedMessage) {
|
||||
handle((DepositTxPublishedMessage) message);
|
||||
}
|
||||
},
|
||||
(errorMessage -> {
|
||||
@ -217,30 +212,22 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
|
||||
// Massage dispatcher
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void handleMessage(Message message, Peer sender) {
|
||||
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender);
|
||||
if (message instanceof TradeMessage) {
|
||||
TradeMessage tradeMessage = (TradeMessage) message;
|
||||
nonEmptyStringOf(tradeMessage.tradeId);
|
||||
|
||||
if (tradeMessage.tradeId.equals(processModel.getId())) {
|
||||
if (tradeMessage instanceof RequestPayDepositMessage) {
|
||||
handle((RequestPayDepositMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof DepositTxPublishedMessage) {
|
||||
handle((DepositTxPublishedMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof FiatTransferStartedMessage) {
|
||||
handle((FiatTransferStartedMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof PayoutTxFinalizedMessage) {
|
||||
handle((PayoutTxFinalizedMessage) tradeMessage);
|
||||
}
|
||||
else {
|
||||
|
||||
log.error("Incoming message not supported. " + tradeMessage);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender) {
|
||||
if (tradeMessage instanceof RequestPayDepositMessage) {
|
||||
handle((RequestPayDepositMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof DepositTxPublishedMessage) {
|
||||
handle((DepositTxPublishedMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof FiatTransferStartedMessage) {
|
||||
handle((FiatTransferStartedMessage) tradeMessage);
|
||||
}
|
||||
else if (tradeMessage instanceof PayoutTxFinalizedMessage) {
|
||||
handle((PayoutTxFinalizedMessage) tradeMessage);
|
||||
}
|
||||
else {
|
||||
log.error("Incoming message not supported. " + tradeMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,51 +19,67 @@ package io.bitsquare.trade.protocol.trade;
|
||||
|
||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.p2p.MailboxMessage;
|
||||
import io.bitsquare.p2p.MessageHandler;
|
||||
import io.bitsquare.crypto.MessageWithPubKey;
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.p2p.DecryptedMessageHandler;
|
||||
import io.bitsquare.p2p.Message;
|
||||
import io.bitsquare.p2p.Peer;
|
||||
import io.bitsquare.p2p.listener.GetPeerAddressListener;
|
||||
import io.bitsquare.trade.OffererTrade;
|
||||
import io.bitsquare.trade.TakerTrade;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
||||
import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener;
|
||||
import io.bitsquare.trade.states.OffererTradeState;
|
||||
import io.bitsquare.trade.states.TakerTradeState;
|
||||
|
||||
import org.bitcoinj.utils.Threading;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static io.bitsquare.util.Validator.nonEmptyStringOf;
|
||||
|
||||
public abstract class TradeProtocol {
|
||||
private static final Logger log = LoggerFactory.getLogger(TradeProtocol.class);
|
||||
private static final long TIMEOUT = 10000;
|
||||
|
||||
protected final ProcessModel processModel;
|
||||
protected MessageHandler messageHandler;
|
||||
private DecryptedMessageHandler decryptedMessageHandler;
|
||||
protected Timer timeoutTimer;
|
||||
protected Trade trade;
|
||||
|
||||
public TradeProtocol(ProcessModel processModel) {
|
||||
this.processModel = processModel;
|
||||
|
||||
decryptedMessageHandler = this::handleMessageWithPubKey;
|
||||
|
||||
processModel.getMessageService().addDecryptedMessageHandler(decryptedMessageHandler);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
log.debug("cleanup " + this);
|
||||
stopTimeout();
|
||||
processModel.getMessageService().removeMessageHandler(messageHandler);
|
||||
if (decryptedMessageHandler != null)
|
||||
processModel.getMessageService().removeDecryptedMessageHandler(decryptedMessageHandler);
|
||||
}
|
||||
|
||||
abstract public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade);
|
||||
public void applyMailboxMessage(MessageWithPubKey messageWithPubKey, Trade trade) {
|
||||
log.debug("applyMailboxMessage " + messageWithPubKey.getMessage());
|
||||
if (messageWithPubKey.getSignaturePubKey().equals(processModel.tradingPeer.getPubKeyRing().getMsgSignaturePubKey()))
|
||||
doApplyMailboxMessage(messageWithPubKey.getMessage(), trade);
|
||||
else
|
||||
log.error("SignaturePubKey in message does not match the SignaturePubKey we have stored to that trading peer.");
|
||||
}
|
||||
|
||||
protected void findPeerAddress(PublicKey p2pSigPubKey, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
protected abstract void doApplyMailboxMessage(Message message, Trade trade);
|
||||
|
||||
protected void findPeerAddress(PubKeyRing pubKeyRing, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
try {
|
||||
processModel.getAddressService().findPeerAddress(p2pSigPubKey, new GetPeerAddressListener() {
|
||||
processModel.getAddressService().findPeerAddress(pubKeyRing, new GetPeerAddressListener() {
|
||||
@Override
|
||||
public void onResult(Peer peer) {
|
||||
trade.setTradingPeer(peer);
|
||||
@ -80,6 +96,28 @@ public abstract class TradeProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleMessageWithPubKey(MessageWithPubKey messageWithPubKey, Peer sender) {
|
||||
// We check the sig only as soon we have stored the peers pubKeyRing.
|
||||
if (processModel.tradingPeer.getPubKeyRing() != null &&
|
||||
!messageWithPubKey.getSignaturePubKey().equals(processModel.tradingPeer.getPubKeyRing().getMsgSignaturePubKey())) {
|
||||
log.error("Signature used in seal message does not match the one stored with that trade for the trading peer.");
|
||||
}
|
||||
else {
|
||||
Message message = messageWithPubKey.getMessage();
|
||||
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender);
|
||||
if (message instanceof TradeMessage) {
|
||||
TradeMessage tradeMessage = (TradeMessage) message;
|
||||
nonEmptyStringOf(tradeMessage.tradeId);
|
||||
|
||||
if (tradeMessage.tradeId.equals(processModel.getId())) {
|
||||
doHandleDecryptedMessage(tradeMessage, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void doHandleDecryptedMessage(TradeMessage tradeMessage, Peer sender);
|
||||
|
||||
public void checkPayoutTxTimeLock(Trade trade) {
|
||||
this.trade = trade;
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
package io.bitsquare.trade.protocol.trade;
|
||||
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
@ -26,8 +27,6 @@ import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@ -41,9 +40,6 @@ public class TradingPeer implements Serializable {
|
||||
|
||||
// Mutable
|
||||
private String accountId;
|
||||
private PublicKey p2pSigPubKey;
|
||||
private PublicKey p2pEncryptPubKey;
|
||||
private byte[] tradeWalletPubKey;
|
||||
private FiatAccount fiatAccount;
|
||||
private Transaction preparedDepositTx;
|
||||
private List<TransactionOutput> connectedOutputsForAllInputs;
|
||||
@ -54,6 +50,8 @@ public class TradingPeer implements Serializable {
|
||||
private String contractAsJson;
|
||||
private String contractSignature;
|
||||
private byte[] signature;
|
||||
private PubKeyRing pubKeyRing;
|
||||
private byte[] tradeWalletPubKey;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -81,23 +79,7 @@ public class TradingPeer implements Serializable {
|
||||
public void setAccountId(String accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public PublicKey getP2pSigPubKey() {
|
||||
return p2pSigPubKey;
|
||||
}
|
||||
|
||||
public void setP2pSigPubKey(PublicKey p2pSigPubKey) {
|
||||
this.p2pSigPubKey = p2pSigPubKey;
|
||||
}
|
||||
|
||||
public PublicKey getP2pEncryptPubKey() {
|
||||
return p2pEncryptPubKey;
|
||||
}
|
||||
|
||||
public void setP2pEncryptPubKey(PublicKey p2pEncryptPubKey) {
|
||||
this.p2pEncryptPubKey = p2pEncryptPubKey;
|
||||
}
|
||||
|
||||
|
||||
public byte[] getTradeWalletPubKey() {
|
||||
return tradeWalletPubKey;
|
||||
}
|
||||
@ -105,7 +87,7 @@ public class TradingPeer implements Serializable {
|
||||
public void setTradeWalletPubKey(byte[] tradeWalletPubKey) {
|
||||
this.tradeWalletPubKey = tradeWalletPubKey;
|
||||
}
|
||||
|
||||
|
||||
public FiatAccount getFiatAccount() {
|
||||
return fiatAccount;
|
||||
}
|
||||
@ -185,4 +167,12 @@ public class TradingPeer implements Serializable {
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public PubKeyRing getPubKeyRing() {
|
||||
return pubKeyRing;
|
||||
}
|
||||
|
||||
public void setPubKeyRing(PubKeyRing pubKeyRing) {
|
||||
this.pubKeyRing = pubKeyRing;
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
package io.bitsquare.trade.protocol.trade.messages;
|
||||
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import java.io.Serializable;
|
||||
@ -29,11 +31,14 @@ public class RequestDepositTxInputsMessage extends TradeMessage implements Seria
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public final Coin tradeAmount;
|
||||
public final PubKeyRing sellerPubKeyRing;
|
||||
public final String sellerOfferFeeTxId;
|
||||
public final byte[] sellerTradeWalletPubKey;
|
||||
|
||||
public RequestDepositTxInputsMessage(String tradeId, String sellerOfferFeeTxId, Coin tradeAmount, byte[] sellerTradeWalletPubKey) {
|
||||
public RequestDepositTxInputsMessage(String tradeId, PubKeyRing sellerPubKeyRing, String sellerOfferFeeTxId, Coin tradeAmount, byte[]
|
||||
sellerTradeWalletPubKey) {
|
||||
super(tradeId);
|
||||
this.sellerPubKeyRing = sellerPubKeyRing;
|
||||
this.sellerOfferFeeTxId = sellerOfferFeeTxId;
|
||||
this.tradeAmount = tradeAmount;
|
||||
this.sellerTradeWalletPubKey = sellerTradeWalletPubKey;
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
package io.bitsquare.trade.protocol.trade.messages;
|
||||
|
||||
import io.bitsquare.crypto.PubKeyRing;
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
@ -24,8 +25,6 @@ import org.bitcoinj.core.TransactionOutput;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
@ -38,8 +37,7 @@ public class RequestPayDepositMessage extends TradeMessage implements Serializab
|
||||
public final List<TransactionOutput> buyerConnectedOutputsForAllInputs;
|
||||
public final List<TransactionOutput> buyerOutputs;
|
||||
public final byte[] buyerTradeWalletPubKey;
|
||||
public final PublicKey buyerP2pSigPublicKey;
|
||||
public final PublicKey buyerP2pEncryptPublicKey;
|
||||
public final PubKeyRing buyerPubKeyRing;
|
||||
public final FiatAccount buyerFiatAccount;
|
||||
public final String buyerAccountId;
|
||||
public final Coin tradeAmount;
|
||||
@ -49,14 +47,12 @@ public class RequestPayDepositMessage extends TradeMessage implements Serializab
|
||||
List<TransactionOutput> buyerConnectedOutputsForAllInputs,
|
||||
List<TransactionOutput> buyerOutputs,
|
||||
byte[] buyerTradeWalletPubKey,
|
||||
PublicKey buyerP2pSigPublicKey,
|
||||
PublicKey buyerP2pEncryptPublicKey,
|
||||
PubKeyRing buyerPubKeyRing,
|
||||
FiatAccount buyerFiatAccount,
|
||||
String buyerAccountId) {
|
||||
super(tradeId);
|
||||
this.tradeAmount = tradeAmount;
|
||||
this.buyerP2pSigPublicKey = buyerP2pSigPublicKey;
|
||||
this.buyerP2pEncryptPublicKey = buyerP2pEncryptPublicKey;
|
||||
this.buyerPubKeyRing = buyerPubKeyRing;
|
||||
this.buyerConnectedOutputsForAllInputs = buyerConnectedOutputsForAllInputs;
|
||||
this.buyerOutputs = buyerOutputs;
|
||||
this.buyerTradeWalletPubKey = buyerTradeWalletPubKey;
|
||||
|
@ -24,8 +24,6 @@ import org.bitcoinj.core.TransactionOutput;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
@ -37,8 +35,6 @@ public class RequestPublishDepositTxMessage extends TradeMessage implements Seri
|
||||
|
||||
public final FiatAccount sellerFiatAccount;
|
||||
public final String sellerAccountId;
|
||||
public final PublicKey sellerP2pSigPublicKey;
|
||||
public final PublicKey sellerP2pEncryptPublicKey;
|
||||
public final String sellerContractAsJson;
|
||||
public final String sellerContractSignature;
|
||||
public final String sellerPayoutAddressString;
|
||||
@ -50,8 +46,6 @@ public class RequestPublishDepositTxMessage extends TradeMessage implements Seri
|
||||
FiatAccount sellerFiatAccount,
|
||||
String sellerAccountId,
|
||||
byte[] sellerTradeWalletPubKey,
|
||||
PublicKey sellerP2pSigPublicKey,
|
||||
PublicKey sellerP2pEncryptPublicKey,
|
||||
String sellerContractAsJson,
|
||||
String sellerContractSignature,
|
||||
String sellerPayoutAddressString,
|
||||
@ -61,8 +55,6 @@ public class RequestPublishDepositTxMessage extends TradeMessage implements Seri
|
||||
this.sellerFiatAccount = sellerFiatAccount;
|
||||
this.sellerAccountId = sellerAccountId;
|
||||
this.sellerTradeWalletPubKey = sellerTradeWalletPubKey;
|
||||
this.sellerP2pSigPublicKey = sellerP2pSigPublicKey;
|
||||
this.sellerP2pEncryptPublicKey = sellerP2pEncryptPublicKey;
|
||||
this.sellerContractAsJson = sellerContractAsJson;
|
||||
this.sellerContractSignature = sellerContractSignature;
|
||||
this.sellerPayoutAddressString = sellerPayoutAddressString;
|
||||
|
@ -43,8 +43,9 @@ public class ProcessRequestDepositTxInputsMessage extends TradeTask {
|
||||
checkTradeId(processModel.getId(), message);
|
||||
checkNotNull(message);
|
||||
|
||||
trade.setTradeAmount(positiveCoinOf(nonZeroCoinOf(message.tradeAmount)));
|
||||
processModel.tradingPeer.setPubKeyRing(checkNotNull(message.sellerPubKeyRing));
|
||||
processModel.setTakeOfferFeeTxId(nonEmptyStringOf(message.sellerOfferFeeTxId));
|
||||
trade.setTradeAmount(positiveCoinOf(nonZeroCoinOf(message.tradeAmount)));
|
||||
processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(message.sellerTradeWalletPubKey));
|
||||
|
||||
complete();
|
||||
|
@ -45,9 +45,7 @@ public class ProcessRequestPublishDepositTxMessage extends TradeTask {
|
||||
|
||||
processModel.tradingPeer.setFiatAccount(checkNotNull(message.sellerFiatAccount));
|
||||
processModel.tradingPeer.setAccountId(nonEmptyStringOf(message.sellerAccountId));
|
||||
processModel.tradingPeer.setP2pSigPubKey(checkNotNull(message.sellerP2pSigPublicKey));
|
||||
processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(message.sellerTradeWalletPubKey));
|
||||
processModel.tradingPeer.setP2pEncryptPubKey(checkNotNull(message.sellerP2pEncryptPublicKey));
|
||||
processModel.tradingPeer.setContractAsJson(nonEmptyStringOf(message.sellerContractAsJson));
|
||||
processModel.tradingPeer.setContractSignature(nonEmptyStringOf(message.sellerContractSignature));
|
||||
processModel.tradingPeer.setPayoutAddressString(nonEmptyStringOf(message.sellerPayoutAddressString));
|
||||
|
@ -39,24 +39,28 @@ public class SendDepositTxPublishedMessage extends TradeTask {
|
||||
try {
|
||||
DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(processModel.getId(), trade.getDepositTx());
|
||||
|
||||
processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage, new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
log.trace("DepositTxPublishedMessage successfully arrived at peer");
|
||||
processModel.getMessageService().sendEncryptedMessage(
|
||||
trade.getTradingPeer(),
|
||||
processModel.tradingPeer.getPubKeyRing(),
|
||||
tradeMessage,
|
||||
new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
log.trace("DepositTxPublishedMessage successfully arrived at peer");
|
||||
|
||||
complete();
|
||||
}
|
||||
complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFault() {
|
||||
appendToErrorMessage("Sending DepositTxPublishedMessage failed");
|
||||
trade.setErrorMessage(errorMessage);
|
||||
@Override
|
||||
public void handleFault() {
|
||||
appendToErrorMessage("Sending DepositTxPublishedMessage failed");
|
||||
trade.setErrorMessage(errorMessage);
|
||||
|
||||
StateUtil.setSendFailedState(trade);
|
||||
StateUtil.setSendFailedState(trade);
|
||||
|
||||
failed();
|
||||
}
|
||||
});
|
||||
failed();
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
trade.setThrowable(t);
|
||||
|
@ -45,10 +45,10 @@ public class SendFiatTransferStartedMessage extends TradeTask {
|
||||
processModel.getAddressEntry().getAddressString()
|
||||
);
|
||||
|
||||
processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage,
|
||||
processModel.tradingPeer.getP2pSigPubKey(),
|
||||
processModel.tradingPeer.getP2pEncryptPubKey(),
|
||||
processModel.getRegistrationKeyPair(),
|
||||
processModel.getMessageService().sendEncryptedMessage(
|
||||
trade.getTradingPeer(),
|
||||
processModel.tradingPeer.getPubKeyRing(),
|
||||
tradeMessage,
|
||||
new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
|
@ -38,11 +38,10 @@ public class SendPayoutTxFinalizedMessage extends TradeTask {
|
||||
protected void doRun() {
|
||||
try {
|
||||
PayoutTxFinalizedMessage tradeMessage = new PayoutTxFinalizedMessage(processModel.getId(), trade.getPayoutTx());
|
||||
processModel.getMessageService().sendMessage(trade.getTradingPeer(),
|
||||
processModel.getMessageService().sendEncryptedMessage(
|
||||
trade.getTradingPeer(),
|
||||
processModel.tradingPeer.getPubKeyRing(),
|
||||
tradeMessage,
|
||||
processModel.tradingPeer.getP2pSigPubKey(),
|
||||
processModel.tradingPeer.getP2pEncryptPubKey(),
|
||||
processModel.getRegistrationKeyPair(),
|
||||
new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
|
@ -43,27 +43,30 @@ public class SendRequestPayDepositMessage extends TradeTask {
|
||||
processModel.getConnectedOutputsForAllInputs(),
|
||||
processModel.getOutputs(),
|
||||
processModel.getTradeWalletPubKey(),
|
||||
processModel.getP2pSigPubKey(),
|
||||
processModel.getP2pEncryptPubKey(),
|
||||
processModel.getPubKeyRing(),
|
||||
processModel.getFiatAccount(),
|
||||
processModel.getAccountId());
|
||||
|
||||
processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage, new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
log.trace("RequestTakerDepositPaymentMessage successfully arrived at peer");
|
||||
complete();
|
||||
}
|
||||
processModel.getMessageService().sendEncryptedMessage(
|
||||
trade.getTradingPeer(),
|
||||
processModel.tradingPeer.getPubKeyRing(),
|
||||
tradeMessage,
|
||||
new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
log.trace("RequestTakerDepositPaymentMessage successfully arrived at peer");
|
||||
complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFault() {
|
||||
appendToErrorMessage("Sending RequestTakerDepositPaymentMessage failed");
|
||||
trade.setErrorMessage(errorMessage);
|
||||
StateUtil.setOfferOpenState(trade);
|
||||
StateUtil.setSendFailedState(trade);
|
||||
failed();
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void handleFault() {
|
||||
appendToErrorMessage("Sending RequestTakerDepositPaymentMessage failed");
|
||||
trade.setErrorMessage(errorMessage);
|
||||
StateUtil.setOfferOpenState(trade);
|
||||
StateUtil.setSendFailedState(trade);
|
||||
failed();
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
trade.setThrowable(t);
|
||||
|
@ -45,10 +45,10 @@ public class VerifyAndSignContract extends TradeTask {
|
||||
processModel.tradingPeer.getAccountId(),
|
||||
processModel.getFiatAccount(),
|
||||
processModel.tradingPeer.getFiatAccount(),
|
||||
processModel.getP2pSigPubKey(),
|
||||
processModel.tradingPeer.getP2pSigPubKey());
|
||||
processModel.getPubKeyRing(),
|
||||
processModel.tradingPeer.getPubKeyRing());
|
||||
String contractAsJson = Utilities.objectToJson(contract);
|
||||
String signature = processModel.getSignatureService().signMessage(processModel.getRegistrationKeyPair(), contractAsJson);
|
||||
String signature = processModel.getCryptoService().signMessage(processModel.getRegistrationKeyPair(), contractAsJson);
|
||||
trade.setContract(contract);
|
||||
trade.setContractAsJson(contractAsJson);
|
||||
trade.setBuyerContractSignature(signature);
|
||||
|
@ -45,10 +45,10 @@ public class CreateAndSignContract extends TradeTask {
|
||||
processModel.getAccountId(),
|
||||
processModel.tradingPeer.getFiatAccount(),
|
||||
processModel.getFiatAccount(),
|
||||
processModel.tradingPeer.getP2pSigPubKey(),
|
||||
processModel.getP2pSigPubKey());
|
||||
processModel.tradingPeer.getPubKeyRing(),
|
||||
processModel.getPubKeyRing());
|
||||
String contractAsJson = Utilities.objectToJson(contract);
|
||||
String signature = processModel.getSignatureService().signMessage(processModel.getRegistrationKeyPair(), contractAsJson);
|
||||
String signature = processModel.getCryptoService().signMessage(processModel.getRegistrationKeyPair(), contractAsJson);
|
||||
|
||||
trade.setContract(contract);
|
||||
trade.setContractAsJson(contractAsJson);
|
||||
|
@ -46,8 +46,7 @@ public class ProcessRequestPayDepositMessage extends TradeTask {
|
||||
checkArgument(message.buyerConnectedOutputsForAllInputs.size() > 0);
|
||||
processModel.tradingPeer.setOutputs(checkNotNull(message.buyerOutputs));
|
||||
processModel.tradingPeer.setTradeWalletPubKey(checkNotNull(message.buyerTradeWalletPubKey));
|
||||
processModel.tradingPeer.setP2pSigPubKey(checkNotNull(message.buyerP2pSigPublicKey));
|
||||
processModel.tradingPeer.setP2pEncryptPubKey(checkNotNull(message.buyerP2pEncryptPublicKey));
|
||||
processModel.tradingPeer.setPubKeyRing(checkNotNull(message.buyerPubKeyRing));
|
||||
processModel.tradingPeer.setFiatAccount(checkNotNull(message.buyerFiatAccount));
|
||||
processModel.tradingPeer.setAccountId(nonEmptyStringOf(message.buyerAccountId));
|
||||
trade.setTradeAmount(positiveCoinOf(nonZeroCoinOf(message.tradeAmount)));
|
||||
|
@ -44,37 +44,42 @@ public class SendRequestDepositTxInputsMessage extends TradeTask {
|
||||
assert processModel.getTakeOfferFeeTx() != null;
|
||||
RequestDepositTxInputsMessage message = new RequestDepositTxInputsMessage(
|
||||
processModel.getId(),
|
||||
processModel.getPubKeyRing(),
|
||||
processModel.getTakeOfferFeeTx().getHashAsString(),
|
||||
trade.getTradeAmount(),
|
||||
processModel.getTradeWalletPubKey());
|
||||
|
||||
processModel.getMessageService().sendMessage(trade.getTradingPeer(), message, new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
log.trace("Sending TakeOfferFeePayedMessage succeeded.");
|
||||
complete();
|
||||
}
|
||||
processModel.getMessageService().sendEncryptedMessage(
|
||||
trade.getTradingPeer(),
|
||||
processModel.tradingPeer.getPubKeyRing(),
|
||||
message,
|
||||
new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
log.trace("Sending TakeOfferFeePayedMessage succeeded.");
|
||||
complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFault() {
|
||||
log.warn("Sending TakeOfferFeePayedMessage failed. We try a second time.");
|
||||
// Take offer fee is already paid, so we need to try to get that trade to succeed.
|
||||
// We try to repeat once and if that fails as well we persist the state for a later retry.
|
||||
if (retryCounter == 0) {
|
||||
retryCounter++;
|
||||
Threading.USER_THREAD.execute(SendRequestDepositTxInputsMessage.this::doRun);
|
||||
}
|
||||
else {
|
||||
appendToErrorMessage("Sending TakeOfferFeePayedMessage to offerer failed. Maybe the network connection was " +
|
||||
"lost or the offerer lost his connection. We persisted the state of the trade, please try again later " +
|
||||
"or cancel that trade.");
|
||||
@Override
|
||||
public void handleFault() {
|
||||
log.warn("Sending TakeOfferFeePayedMessage failed. We try a second time.");
|
||||
// Take offer fee is already paid, so we need to try to get that trade to succeed.
|
||||
// We try to repeat once and if that fails as well we persist the state for a later retry.
|
||||
if (retryCounter == 0) {
|
||||
retryCounter++;
|
||||
Threading.USER_THREAD.execute(SendRequestDepositTxInputsMessage.this::doRun);
|
||||
}
|
||||
else {
|
||||
appendToErrorMessage("Sending TakeOfferFeePayedMessage to offerer failed. Maybe the network connection was " +
|
||||
"lost or the offerer lost his connection. We persisted the state of the trade, please try again later " +
|
||||
"or cancel that trade.");
|
||||
|
||||
trade.setErrorMessage(errorMessage);
|
||||
StateUtil.setSendFailedState(trade);
|
||||
failed();
|
||||
}
|
||||
}
|
||||
});
|
||||
trade.setErrorMessage(errorMessage);
|
||||
StateUtil.setSendFailedState(trade);
|
||||
failed();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
trade.setThrowable(t);
|
||||
|
@ -48,11 +48,10 @@ public class SendRequestFinalizePayoutTxMessage extends TradeTask {
|
||||
trade.getLockTime()
|
||||
);
|
||||
|
||||
processModel.getMessageService().sendMessage(trade.getTradingPeer(),
|
||||
processModel.getMessageService().sendEncryptedMessage(
|
||||
trade.getTradingPeer(),
|
||||
processModel.tradingPeer.getPubKeyRing(),
|
||||
message,
|
||||
processModel.tradingPeer.getP2pSigPubKey(),
|
||||
processModel.tradingPeer.getP2pEncryptPubKey(),
|
||||
processModel.getRegistrationKeyPair(),
|
||||
new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
|
@ -42,8 +42,6 @@ public class SendRequestPublishDepositTxMessage extends TradeTask {
|
||||
processModel.getFiatAccount(),
|
||||
processModel.getAccountId(),
|
||||
processModel.getTradeWalletPubKey(),
|
||||
processModel.getP2pSigPubKey(),
|
||||
processModel.getP2pEncryptPubKey(),
|
||||
trade.getContractAsJson(),
|
||||
trade.getSellerContractSignature(),
|
||||
processModel.getAddressEntry().getAddressString(),
|
||||
@ -51,22 +49,26 @@ public class SendRequestPublishDepositTxMessage extends TradeTask {
|
||||
processModel.getConnectedOutputsForAllInputs()
|
||||
);
|
||||
|
||||
processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage, new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
complete();
|
||||
}
|
||||
processModel.getMessageService().sendEncryptedMessage(
|
||||
trade.getTradingPeer(),
|
||||
processModel.tradingPeer.getPubKeyRing(),
|
||||
tradeMessage,
|
||||
new SendMessageListener() {
|
||||
@Override
|
||||
public void handleResult() {
|
||||
complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFault() {
|
||||
appendToErrorMessage("Sending RequestOffererPublishDepositTxMessage failed");
|
||||
trade.setErrorMessage(errorMessage);
|
||||
@Override
|
||||
public void handleFault() {
|
||||
appendToErrorMessage("Sending RequestOffererPublishDepositTxMessage failed");
|
||||
trade.setErrorMessage(errorMessage);
|
||||
|
||||
StateUtil.setSendFailedState(trade);
|
||||
StateUtil.setSendFailedState(trade);
|
||||
|
||||
failed();
|
||||
}
|
||||
});
|
||||
failed();
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
trade.setThrowable(t);
|
||||
|
@ -17,18 +17,12 @@
|
||||
|
||||
package io.bitsquare.user;
|
||||
|
||||
import io.bitsquare.crypto.EncryptionService;
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.storage.Storage;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -56,28 +50,24 @@ public class User implements Serializable {
|
||||
|
||||
transient private static final Logger log = LoggerFactory.getLogger(User.class);
|
||||
|
||||
transient private Storage<User> storage;
|
||||
// Transient immutable fields
|
||||
transient final private Storage<User> storage;
|
||||
|
||||
// Persisted fields
|
||||
private KeyPair p2pSigKeyPair;
|
||||
private KeyPair p2pEncryptKeyPair;
|
||||
private String accountID;
|
||||
private List<FiatAccount> fiatAccounts = new ArrayList<>();
|
||||
private FiatAccount currentFiatAccount;
|
||||
|
||||
// Observable wrappers
|
||||
final transient private ObservableList<FiatAccount> fiatAccountsObservableList = FXCollections.observableArrayList(fiatAccounts);
|
||||
final transient private ObjectProperty<FiatAccount> currentFiatAccountProperty = new SimpleObjectProperty<>(currentFiatAccount);
|
||||
transient final private ObservableList<FiatAccount> fiatAccountsObservableList = FXCollections.observableArrayList(fiatAccounts);
|
||||
transient final private ObjectProperty<FiatAccount> currentFiatAccountProperty = new SimpleObjectProperty<>(currentFiatAccount);
|
||||
|
||||
@Inject
|
||||
public User(Storage<User> storage, EncryptionService encryptionService) {
|
||||
public User(Storage<User> storage) throws NoSuchAlgorithmException {
|
||||
this.storage = storage;
|
||||
EncryptionService encryptionService1 = encryptionService;
|
||||
|
||||
User persisted = storage.initAndGetPersisted(this);
|
||||
if (persisted != null) {
|
||||
p2pSigKeyPair = persisted.getP2pSigKeyPair();
|
||||
p2pEncryptKeyPair = persisted.getP2pEncryptKeyPair();
|
||||
accountID = persisted.getAccountId();
|
||||
|
||||
fiatAccounts = new ArrayList<>(persisted.getFiatAccounts());
|
||||
@ -86,17 +76,6 @@ public class User implements Serializable {
|
||||
currentFiatAccount = persisted.getCurrentFiatAccount();
|
||||
currentFiatAccountProperty.set(currentFiatAccount);
|
||||
}
|
||||
else {
|
||||
// First time we create key pairs
|
||||
try {
|
||||
p2pSigKeyPair = encryptionService.getGeneratedDSAKeyPair();
|
||||
p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
storage.queueUpForSave();
|
||||
// Use that to guarantee update of the serializable field and to make a storage update in case of a change
|
||||
fiatAccountsObservableList.addListener((ListChangeListener<FiatAccount>) change -> {
|
||||
@ -111,6 +90,7 @@ public class User implements Serializable {
|
||||
|
||||
// for unit tests
|
||||
public User() {
|
||||
this.storage = null;
|
||||
}
|
||||
|
||||
|
||||
@ -196,26 +176,6 @@ public class User implements Serializable {
|
||||
return getAccountId() != null;
|
||||
}
|
||||
|
||||
public KeyPair getP2pSigKeyPair() {
|
||||
return p2pSigKeyPair;
|
||||
}
|
||||
|
||||
public PublicKey getP2pSigPubKey() {
|
||||
return p2pSigKeyPair.getPublic();
|
||||
}
|
||||
|
||||
public PublicKey getP2pEncryptPubKey() {
|
||||
return p2pEncryptKeyPair.getPublic();
|
||||
}
|
||||
|
||||
public PrivateKey getP2pEncryptPrivateKey() {
|
||||
return p2pEncryptKeyPair.getPrivate();
|
||||
}
|
||||
|
||||
private KeyPair getP2pEncryptKeyPair() {
|
||||
return p2pEncryptKeyPair;
|
||||
}
|
||||
|
||||
private List<FiatAccount> getFiatAccounts() {
|
||||
return fiatAccounts;
|
||||
}
|
||||
@ -231,5 +191,4 @@ public class User implements Serializable {
|
||||
public ObservableList<FiatAccount> fiatAccountsObservableList() {
|
||||
return fiatAccountsObservableList;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import java.awt.*;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectInputStream;
|
||||
@ -200,6 +201,15 @@ public class Utilities {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void deleteDirectory(File file) throws IOException {
|
||||
if (file.isDirectory()) {
|
||||
for (File c : file.listFiles())
|
||||
deleteDirectory(c);
|
||||
}
|
||||
if (!file.delete())
|
||||
throw new FileNotFoundException("Failed to delete file: " + file);
|
||||
}
|
||||
|
||||
private static void printElapsedTime(String msg) {
|
||||
if (!msg.isEmpty()) {
|
||||
msg += " / ";
|
||||
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.crypto;
|
||||
|
||||
import io.bitsquare.p2p.MailboxMessage;
|
||||
import io.bitsquare.util.Utilities;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class CryptoServiceTests {
|
||||
private static final Logger log = LoggerFactory.getLogger(CryptoServiceTests.class);
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private PubKeyRing pubKeyRing;
|
||||
private KeyRing keyRing;
|
||||
private File dir = new File("/tmp/bitsquare_tests");
|
||||
|
||||
@Before
|
||||
public void setup() throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, CryptoException {
|
||||
dir.mkdir();
|
||||
KeyStorage keyStorage = new KeyStorage(dir);
|
||||
keyRing = new KeyRing(keyStorage);
|
||||
pubKeyRing = keyRing.getPubKeyRing();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws IOException {
|
||||
Utilities.deleteDirectory(dir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecryptAndVerifyMessage() throws CryptoException {
|
||||
CryptoService<MailboxMessage> encryptionService = new CryptoService<>(keyRing);
|
||||
TestMessage data = new TestMessage("test");
|
||||
SealedAndSignedMessage encrypted = encryptionService.encryptAndSignMessage(pubKeyRing, data);
|
||||
MessageWithPubKey decrypted = encryptionService.decryptAndVerifyMessage(encrypted);
|
||||
assertEquals("", data.data, ((TestMessage) decrypted.getMessage()).data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestMessage implements MailboxMessage {
|
||||
public String data = "test";
|
||||
|
||||
public TestMessage(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
/*
|
||||
* This file is part of Bitsquare.
|
||||
*
|
||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bitsquare.crypto;
|
||||
|
||||
import io.bitsquare.p2p.MailboxMessage;
|
||||
|
||||
import org.bitcoinj.core.ECKey;
|
||||
|
||||
import java.security.KeyPair;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class EncryptionServiceTests {
|
||||
private static final Logger log = LoggerFactory.getLogger(EncryptionServiceTests.class);
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testEncryptionWithMailboxMessage() throws Exception {
|
||||
SignatureService signatureService = new SignatureService();
|
||||
EncryptionService<MailboxMessage> encryptionService = new EncryptionService<>(signatureService);
|
||||
KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair();
|
||||
ECKey signatureKeyPair = new ECKey();
|
||||
|
||||
TestMessage data = new TestMessage("test");
|
||||
byte[] encrypted = encryptionService.encryptMessage(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data);
|
||||
MailboxMessage decrypted = encryptionService.decryptToMessage(p2pEncryptKeyPair.getPrivate(), encrypted);
|
||||
assertEquals("", data.data, ((TestMessage) decrypted).data);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptionWithInteger() throws Exception {
|
||||
SignatureService signatureService = new SignatureService();
|
||||
EncryptionService<Integer> encryptionService = new EncryptionService<>(signatureService);
|
||||
KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair();
|
||||
ECKey signatureKeyPair = new ECKey();
|
||||
Integer data = 1234;
|
||||
byte[] encrypted = encryptionService.encryptObject(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data);
|
||||
Integer decrypted = encryptionService.decryptToMessage(p2pEncryptKeyPair.getPrivate(), encrypted);
|
||||
assertEquals("", data, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptionWithBytes() throws Exception {
|
||||
SignatureService signatureService = new SignatureService();
|
||||
EncryptionService<MailboxMessage> encryptionService = new EncryptionService<>(signatureService);
|
||||
KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair();
|
||||
ECKey signatureKeyPair = new ECKey();
|
||||
|
||||
byte[] data = new byte[]{0x00, 0x01, 0x02, 0x03, 0x04};
|
||||
byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data);
|
||||
byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), encrypted);
|
||||
assertTrue(Arrays.equals(data, decrypted));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptionWithLargeData() throws Exception {
|
||||
SignatureService signatureService = new SignatureService();
|
||||
EncryptionService<MailboxMessage> encryptionService = new EncryptionService<>(signatureService);
|
||||
KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair();
|
||||
ECKey signatureKeyPair = new ECKey();
|
||||
|
||||
byte[] data = new byte[2000];
|
||||
new Random().nextBytes(data);
|
||||
|
||||
byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data);
|
||||
byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), encrypted);
|
||||
assertTrue(Arrays.equals(data, decrypted));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptionWithTooMuchData() throws Exception {
|
||||
SignatureService signatureService = new SignatureService();
|
||||
EncryptionService<MailboxMessage> encryptionService = new EncryptionService<>(signatureService);
|
||||
KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair();
|
||||
ECKey signatureKeyPair = new ECKey();
|
||||
|
||||
byte[] data = new byte[11000];
|
||||
new Random().nextBytes(data);
|
||||
|
||||
thrown.expect(CryptoException.class);
|
||||
thrown.expectMessage("The data exceeds the max. size.");
|
||||
|
||||
byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data);
|
||||
byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), encrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptionWithTooLessData() throws Exception {
|
||||
SignatureService signatureService = new SignatureService();
|
||||
EncryptionService<MailboxMessage> encryptionService = new EncryptionService<>(signatureService);
|
||||
KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair();
|
||||
ECKey signatureKeyPair = new ECKey();
|
||||
|
||||
byte[] data = new byte[11000];
|
||||
new Random().nextBytes(data);
|
||||
|
||||
thrown.expect(CryptoException.class);
|
||||
thrown.expectMessage("The data is shorter as the min. overhead length.");
|
||||
|
||||
byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data);
|
||||
byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), new byte[24]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptionWithManipulatedPayload() throws Exception {
|
||||
SignatureService signatureService = new SignatureService();
|
||||
EncryptionService<MailboxMessage> encryptionService = new EncryptionService<>(signatureService);
|
||||
KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair();
|
||||
ECKey signatureKeyPair = new ECKey();
|
||||
|
||||
byte[] data = new byte[110];
|
||||
new Random().nextBytes(data);
|
||||
|
||||
thrown.expect(CryptoException.class);
|
||||
thrown.expectMessage("The checksum is invalid.");
|
||||
|
||||
byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data);
|
||||
byte[] manipulated = new byte[encrypted.length];
|
||||
System.arraycopy(encrypted, 0, manipulated, 0, manipulated.length);
|
||||
manipulated[manipulated.length - 1] = 0x31;
|
||||
byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), manipulated);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptionWithWrongVersion() throws Exception {
|
||||
SignatureService signatureService = new SignatureService();
|
||||
EncryptionService<MailboxMessage> encryptionService = new EncryptionService<>(signatureService);
|
||||
KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair();
|
||||
ECKey signatureKeyPair = new ECKey();
|
||||
|
||||
byte[] data = new byte[1100];
|
||||
new Random().nextBytes(data);
|
||||
|
||||
thrown.expect(CryptoException.class);
|
||||
thrown.expectMessage("Incorrect version.");
|
||||
|
||||
byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data);
|
||||
byte[] manipulated = new byte[encrypted.length];
|
||||
System.arraycopy(encrypted, 0, manipulated, 0, manipulated.length);
|
||||
manipulated[0] = 0x02;
|
||||
byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), manipulated);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptionWithNoData() throws Exception {
|
||||
SignatureService signatureService = new SignatureService();
|
||||
EncryptionService<MailboxMessage> encryptionService = new EncryptionService<>(signatureService);
|
||||
KeyPair p2pEncryptKeyPair = encryptionService.getGeneratedRSAKeyPair();
|
||||
ECKey signatureKeyPair = new ECKey();
|
||||
|
||||
byte[] data = new byte[0];
|
||||
|
||||
thrown.expect(CryptoException.class);
|
||||
thrown.expectMessage("Input data is null.");
|
||||
|
||||
byte[] encrypted = encryptionService.encryptBytes(p2pEncryptKeyPair.getPublic(), signatureKeyPair, data);
|
||||
byte[] decrypted = encryptionService.decryptBytes(p2pEncryptKeyPair.getPrivate(), encrypted);
|
||||
}
|
||||
}
|
||||
|
||||
class TestMessage implements MailboxMessage {
|
||||
public String data = "test";
|
||||
|
||||
public TestMessage(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ package io.bitsquare.app;
|
||||
|
||||
import io.bitsquare.btc.UserAgent;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.crypto.KeyStorage;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.p2p.tomp2p.TomP2PModule;
|
||||
import io.bitsquare.storage.Storage;
|
||||
@ -59,6 +60,7 @@ public class BitsquareAppEnvironment extends BitsquareEnvironment {
|
||||
setProperty(WalletService.PREFIX_KEY, appName);
|
||||
|
||||
setProperty(Storage.DIR_KEY, Paths.get(appDataDir, "db").toString());
|
||||
setProperty(KeyStorage.DIR_KEY, Paths.get(appDataDir, "keys").toString());
|
||||
|
||||
setProperty(MainView.TITLE_KEY, appName);
|
||||
|
||||
|
@ -22,6 +22,8 @@ import io.bitsquare.arbitration.ArbitratorModule;
|
||||
import io.bitsquare.arbitration.tomp2p.TomP2PArbitratorModule;
|
||||
import io.bitsquare.btc.BitcoinModule;
|
||||
import io.bitsquare.crypto.CryptoModule;
|
||||
import io.bitsquare.crypto.KeyRing;
|
||||
import io.bitsquare.crypto.KeyStorage;
|
||||
import io.bitsquare.gui.GuiModule;
|
||||
import io.bitsquare.offer.OfferModule;
|
||||
import io.bitsquare.offer.tomp2p.TomP2POfferModule;
|
||||
@ -59,12 +61,17 @@ class BitsquareAppModule extends BitsquareModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(KeyStorage.class).in(Singleton.class);
|
||||
bind(KeyRing.class).in(Singleton.class);
|
||||
bind(User.class).in(Singleton.class);
|
||||
bind(Preferences.class).in(Singleton.class);
|
||||
bind(AccountSettings.class).in(Singleton.class);
|
||||
|
||||
File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY));
|
||||
bind(File.class).annotatedWith(named(Storage.DIR_KEY)).toInstance(storageDir);
|
||||
|
||||
File keyStorageDir = new File(env.getRequiredProperty(KeyStorage.DIR_KEY));
|
||||
bind(File.class).annotatedWith(named(KeyStorage.DIR_KEY)).toInstance(keyStorageDir);
|
||||
|
||||
bind(Environment.class).toInstance(env);
|
||||
bind(UpdateProcess.class).in(Singleton.class);
|
||||
|
@ -21,6 +21,7 @@ import io.bitsquare.app.UpdateProcess;
|
||||
import io.bitsquare.arbitration.ArbitrationRepository;
|
||||
import io.bitsquare.btc.BitcoinNetwork;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.crypto.KeyRing;
|
||||
import io.bitsquare.fiat.FiatAccount;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
@ -85,6 +86,7 @@ class MainViewModel implements ViewModel {
|
||||
final String bitcoinNetworkAsString;
|
||||
|
||||
private final User user;
|
||||
private KeyRing keyRing;
|
||||
private final WalletService walletService;
|
||||
private final ArbitrationRepository arbitrationRepository;
|
||||
private final ClientNode clientNode;
|
||||
@ -93,10 +95,11 @@ class MainViewModel implements ViewModel {
|
||||
private final BSFormatter formatter;
|
||||
|
||||
@Inject
|
||||
public MainViewModel(User user, WalletService walletService, ArbitrationRepository arbitrationRepository, ClientNode clientNode,
|
||||
public MainViewModel(User user, KeyRing keyRing, WalletService walletService, ArbitrationRepository arbitrationRepository, ClientNode clientNode,
|
||||
TradeManager tradeManager, BitcoinNetwork bitcoinNetwork, UpdateProcess updateProcess,
|
||||
BSFormatter formatter) {
|
||||
this.user = user;
|
||||
this.keyRing = keyRing;
|
||||
this.walletService = walletService;
|
||||
this.arbitrationRepository = arbitrationRepository;
|
||||
this.clientNode = clientNode;
|
||||
@ -137,7 +140,7 @@ class MainViewModel implements ViewModel {
|
||||
// Set executor for all P2PServices
|
||||
BaseP2PService.setUserThread(Platform::runLater);
|
||||
|
||||
Observable<BootstrapState> bootstrapStateAsObservable = clientNode.bootstrap(user.getP2pSigKeyPair());
|
||||
Observable<BootstrapState> bootstrapStateAsObservable = clientNode.bootstrap(keyRing.getDhtSignatureKeyPair());
|
||||
bootstrapStateAsObservable.publish();
|
||||
bootstrapStateAsObservable.subscribe(
|
||||
state -> Platform.runLater(() -> setBootstrapState(state)),
|
||||
|
@ -22,7 +22,7 @@ import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.common.view.InitializableView;
|
||||
import io.bitsquare.trade.protocol.availability.CheckOfferAvailabilityProtocol;
|
||||
import io.bitsquare.trade.protocol.availability.tasks.ProcessReportOfferAvailabilityMessage;
|
||||
import io.bitsquare.trade.protocol.availability.tasks.RequestIsOfferAvailable;
|
||||
import io.bitsquare.trade.protocol.availability.tasks.SendRequestIsOfferAvailableMessage;
|
||||
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol;
|
||||
import io.bitsquare.trade.protocol.placeoffer.tasks.AddOfferToRemoteOfferBook;
|
||||
import io.bitsquare.trade.protocol.placeoffer.tasks.BroadcastCreateOfferFeeTx;
|
||||
@ -82,7 +82,7 @@ public class DebugView extends InitializableView {
|
||||
/*---- Protocol ----*/
|
||||
CheckOfferAvailabilityProtocol.class,
|
||||
io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress.class,
|
||||
RequestIsOfferAvailable.class,
|
||||
SendRequestIsOfferAvailableMessage.class,
|
||||
ProcessReportOfferAvailabilityMessage.class,
|
||||
Boolean.class, /* used as seperator*/
|
||||
|
||||
|
@ -210,7 +210,7 @@ class OfferBookDataModel implements Activatable, DataModel {
|
||||
}
|
||||
|
||||
boolean isMyOffer(Offer offer) {
|
||||
return offer.getP2pSigPubKey() != null && offer.getP2pSigPubKey().equals(user.getP2pSigPubKey());
|
||||
return tradeManager.isMyOffer(offer);
|
||||
}
|
||||
|
||||
Coin getAmountAsCoin() {
|
||||
|
@ -64,8 +64,7 @@ class ClosedTradesDataModel implements Activatable, DataModel {
|
||||
}
|
||||
|
||||
public Offer.Direction getDirection(Offer offer) {
|
||||
return offer.getP2pSigPubKey().equals(user.getP2pSigPubKey()) ?
|
||||
offer.getDirection() : offer.getMirroredDirection();
|
||||
return tradeManager.isMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection();
|
||||
}
|
||||
|
||||
private void applyList() {
|
||||
|
@ -75,8 +75,7 @@ class OpenOffersDataModel implements Activatable, DataModel {
|
||||
}
|
||||
|
||||
public Offer.Direction getDirection(Offer offer) {
|
||||
return offer.getP2pSigPubKey().equals(user.getP2pSigPubKey()) ?
|
||||
offer.getDirection() : offer.getMirroredDirection();
|
||||
return tradeManager.isMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection();
|
||||
}
|
||||
|
||||
private void applyList() {
|
||||
|
@ -145,7 +145,7 @@ class PendingTradesDataModel implements Activatable, DataModel {
|
||||
trade = item.getTrade();
|
||||
tradeProperty.set(trade);
|
||||
|
||||
isOffererRole = trade.getOffer().getP2pSigPubKey().equals(user.getP2pSigPubKey());
|
||||
isOffererRole = tradeManager.isMyOffer(trade.getOffer());
|
||||
|
||||
if (trade instanceof SellerTrade)
|
||||
sellerProcessState.bind(trade.processStateProperty());
|
||||
|
@ -402,6 +402,9 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.error("Unhandled state " + dataModel.getSellerProcessState().get());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBuyerState() {
|
||||
@ -429,7 +432,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
||||
viewState.set(ViewState.BUYER_WAIT_CONFIRM_PAYMENT_RECEIVED);
|
||||
break;
|
||||
|
||||
case PAYOUT_FINALIZED:
|
||||
case PAYOUT_FINALIZED:
|
||||
viewState.set(ViewState.BUYER_PAYOUT_FINALIZED);
|
||||
break;
|
||||
|
||||
@ -474,7 +477,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
||||
viewState.set(ViewState.BUYER_WAIT_CONFIRM_PAYMENT_RECEIVED);
|
||||
break;
|
||||
|
||||
case PAYOUT_FINALIZED:
|
||||
case PAYOUT_FINALIZED:
|
||||
viewState.set(ViewState.BUYER_PAYOUT_FINALIZED);
|
||||
break;
|
||||
|
||||
@ -498,5 +501,8 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.error("Unhandled state " + dataModel.getBuyerProcessState().get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ public class TradeWizardItem extends Button {
|
||||
setMouseTransparent(true);
|
||||
setText(title);
|
||||
setPrefHeight(40);
|
||||
setPrefWidth(340);
|
||||
setPrefWidth(360);
|
||||
setPadding(new Insets(0, 20, 0, 10));
|
||||
setAlignment(Pos.CENTER_LEFT);
|
||||
setDisabled();
|
||||
|
Loading…
x
Reference in New Issue
Block a user