Use encryption/sig for all messages

This commit is contained in:
Manfred Karrer 2015-04-10 23:48:52 +02:00
parent 32c56dad1a
commit 0ec86550b0
68 changed files with 1494 additions and 1152 deletions

View File

@ -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);
}

View File

@ -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) {

View File

@ -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

View File

@ -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);
}
}

View 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);
}
}

View File

@ -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);
}
}
}

View 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;
}
}

View 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);
}
}
}

View File

@ -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;
}
}

View 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()) +
'}';
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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 +

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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();
}

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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

View File

@ -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 + '\'' +*/

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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() {

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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));

View File

@ -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);

View File

@ -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() {

View File

@ -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() {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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)));

View File

@ -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);

View File

@ -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() {

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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 += " / ";

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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)),

View File

@ -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*/

View File

@ -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() {

View File

@ -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() {

View File

@ -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() {

View File

@ -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());

View File

@ -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());
}
}
}

View File

@ -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();