diff --git a/core/src/main/java/io/bitsquare/btc/BlockChainService.java b/core/src/main/java/io/bitsquare/btc/BlockChainService.java index 7c35e0b79d..aea06c63e2 100644 --- a/core/src/main/java/io/bitsquare/btc/BlockChainService.java +++ b/core/src/main/java/io/bitsquare/btc/BlockChainService.java @@ -69,7 +69,7 @@ public class BlockChainService { } @SuppressWarnings("SameReturnValue") - private boolean isBankAccountBlacklisted(FiatAccount fiatAccount) { + private boolean isFiatAccountBlacklisted(FiatAccount fiatAccount) { // TODO check if accountID is on blacklist return false; } diff --git a/core/src/main/java/io/bitsquare/btc/WalletService.java b/core/src/main/java/io/bitsquare/btc/WalletService.java index d0dbf8c59a..81208b09b1 100644 --- a/core/src/main/java/io/bitsquare/btc/WalletService.java +++ b/core/src/main/java/io/bitsquare/btc/WalletService.java @@ -471,14 +471,14 @@ public class WalletService { return tradeWalletService; } - public void payRegistrationFee(String stringifiedBankAccounts, FutureCallback callback) throws + public void payRegistrationFee(String stringifiedFiatAccounts, FutureCallback callback) throws InsufficientMoneyException { log.debug("payRegistrationFee"); - log.trace("stringifiedBankAccounts " + stringifiedBankAccounts); + log.trace("stringifiedFiatAccounts " + stringifiedFiatAccounts); Transaction tx = new Transaction(params); - byte[] data = signatureService.digestMessageWithSignature(getRegistrationAddressEntry().getKeyPair(), stringifiedBankAccounts); + byte[] data = signatureService.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 diff --git a/core/src/main/java/io/bitsquare/crypto/EncryptionPackage.java b/core/src/main/java/io/bitsquare/crypto/EncryptionPackage.java new file mode 100644 index 0000000000..a1970461e2 --- /dev/null +++ b/core/src/main/java/io/bitsquare/crypto/EncryptionPackage.java @@ -0,0 +1,32 @@ +/* + * 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 . + */ + +package io.bitsquare.crypto; + +import java.io.Serializable; + +public class EncryptionPackage implements Serializable { + private static final long serialVersionUID = -8709538217388076762L; + + public final byte[] encryptedKey; + public final byte[] encryptedPayload; + + public EncryptionPackage(byte[] encryptedKey, byte[] encryptedPayload) { + this.encryptedKey = encryptedKey; + this.encryptedPayload = encryptedPayload; + } +} diff --git a/core/src/main/java/io/bitsquare/crypto/EncryptionService.java b/core/src/main/java/io/bitsquare/crypto/EncryptionService.java index 5f1e92d6f1..54b4ea8997 100644 --- a/core/src/main/java/io/bitsquare/crypto/EncryptionService.java +++ b/core/src/main/java/io/bitsquare/crypto/EncryptionService.java @@ -19,11 +19,11 @@ package io.bitsquare.crypto; import io.bitsquare.util.Utilities; -import java.io.Serializable; - +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; @@ -32,98 +32,82 @@ 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; public class EncryptionService { private static final Logger log = LoggerFactory.getLogger(EncryptionService.class); + private static final String ALGO_SYM = "AES"; + private static final String CIPHER_SYM = "AES"; + private static final String ALGO_ASYM = "RSA"; + private static final String CIPHER_ASYM = "RSA/ECB/PKCS1Padding"; + private static final int KEY_SIZE_SYM = 128; + private static final int KEY_SIZE_ASYM = 1024; @Inject public EncryptionService() { } - public KeyPair getKeyPair() { - KeyPair keyPair = null; - try { - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(1024); - keyPair = keyPairGenerator.genKeyPair(); - } catch (Exception e) { - e.printStackTrace(); - log.error("Exception at init " + e.getMessage()); - } - return keyPair; + public KeyPair getKeyPair() throws NoSuchAlgorithmException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGO_ASYM); + keyPairGenerator.initialize(KEY_SIZE_ASYM); + return keyPairGenerator.genKeyPair(); } - public Tuple encryptObject(PublicKey publicKey, Object object) { + public EncryptionPackage encryptObject(PublicKey publicKey, Object object) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, + NoSuchAlgorithmException, NoSuchPaddingException { return encrypt(publicKey, Utilities.objectToBytArray(object)); } - public T decryptToObject(PrivateKey privateKey, Tuple tuple) { - return (T) Utilities.byteArrayToObject(decrypt(privateKey, tuple)); + public T decryptToObject(PrivateKey privateKey, EncryptionPackage encryptionPackage) throws IllegalBlockSizeException, InvalidKeyException, + BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { + return (T) Utilities.byteArrayToObject(decrypt(privateKey, encryptionPackage)); } - public Tuple encrypt(PublicKey publicKey, byte[] payload) { - byte[] encryptedPayload = null; - byte[] encryptedKey = null; - try { - // Create symmetric key and - KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); - keyGenerator.init(256); - SecretKey secretKey = keyGenerator.generateKey(); + public EncryptionPackage encrypt(PublicKey publicKey, byte[] payload) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + BadPaddingException, IllegalBlockSizeException { + // Create symmetric key and + KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGO_SYM); + keyGenerator.init(KEY_SIZE_SYM); + SecretKey secretKey = keyGenerator.generateKey(); - // Encrypt secretKey with asymmetric key - Cipher cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding"); - cipher.init(Cipher.ENCRYPT_MODE, publicKey); - encryptedKey = cipher.doFinal(secretKey.getEncoded()); + // Encrypt secretKey with asymmetric key + Cipher cipherAsym = Cipher.getInstance(CIPHER_ASYM); + cipherAsym.init(Cipher.ENCRYPT_MODE, publicKey); + log.debug("encrypt secret key length: " + secretKey.getEncoded().length); + byte[] encryptedKey = cipherAsym.doFinal(secretKey.getEncoded()); - // Encrypt payload with symmetric key - SecretKeySpec keySpec = new SecretKeySpec(secretKey.getEncoded(), "AES"); - cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, keySpec); - encryptedPayload = cipher.doFinal(payload); - } catch (Exception e) { - e.printStackTrace(); - log.error("Exception at encrypt " + e.getMessage()); - } - return new Tuple(encryptedKey, encryptedPayload); + // Encrypt payload with symmetric key + SecretKeySpec keySpec = new SecretKeySpec(secretKey.getEncoded(), ALGO_SYM); + Cipher cipherSym = Cipher.getInstance(CIPHER_SYM); + cipherSym.init(Cipher.ENCRYPT_MODE, keySpec); + log.debug("encrypt payload length: " + payload.length); + byte[] encryptedPayload = cipherSym.doFinal(payload); + return new EncryptionPackage(encryptedKey, encryptedPayload); } - public byte[] decrypt(PrivateKey privateKey, Tuple tuple) { - byte[] encryptedPayload = tuple.encryptedPayload; - byte[] encryptedKey = tuple.encryptedKey; + public byte[] decrypt(PrivateKey privateKey, EncryptionPackage encryptionPackage) throws NoSuchPaddingException, NoSuchAlgorithmException, + InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + byte[] encryptedPayload = encryptionPackage.encryptedPayload; + byte[] encryptedKey = encryptionPackage.encryptedKey; - byte[] payload = null; - try { - // Decrypt secretKey key with asymmetric key - Cipher cipher = Cipher.getInstance("RSA/NONE/PKCS1Padding"); - cipher.init(Cipher.DECRYPT_MODE, privateKey); - byte[] secretKey = cipher.doFinal(encryptedKey); - - // Decrypt payload with symmetric key - Key key = new SecretKeySpec(secretKey, "AES"); - cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(Cipher.DECRYPT_MODE, key); - payload = cipher.doFinal(encryptedPayload); - } catch (Exception e) { - e.printStackTrace(); - log.error("Exception at decrypt " + e.getMessage()); - } + // Decrypt secretKey key with asymmetric key + Cipher cipherAsym = Cipher.getInstance(CIPHER_ASYM); + cipherAsym.init(Cipher.DECRYPT_MODE, privateKey); + byte[] secretKey = cipherAsym.doFinal(encryptedKey); + log.debug("decrypt secret key length: " + secretKey.length); + // Decrypt payload with symmetric key + Key key = new SecretKeySpec(secretKey, ALGO_SYM); + Cipher cipherSym = Cipher.getInstance(CIPHER_SYM); + cipherSym.init(Cipher.DECRYPT_MODE, key); + byte[] payload = cipherSym.doFinal(encryptedPayload); + log.debug("decrypt payload length: " + payload.length); return payload; } - - public class Tuple implements Serializable { - private static final long serialVersionUID = -8709538217388076762L; - - public final byte[] encryptedKey; - public final byte[] encryptedPayload; - - public Tuple(byte[] encryptedKey, byte[] encryptedPayload) { - this.encryptedKey = encryptedKey; - this.encryptedPayload = encryptedPayload; - } - } } diff --git a/core/src/main/java/io/bitsquare/gui/main/account/content/restrictions/RestrictionsDataModel.java b/core/src/main/java/io/bitsquare/gui/main/account/content/restrictions/RestrictionsDataModel.java index ceacd2b93d..fa1d166f78 100644 --- a/core/src/main/java/io/bitsquare/gui/main/account/content/restrictions/RestrictionsDataModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/account/content/restrictions/RestrictionsDataModel.java @@ -154,7 +154,7 @@ class RestrictionsDataModel implements Activatable, DataModel { private void addMockArbitrator() { if (accountSettings.getAcceptedArbitrators().isEmpty() && user.getP2pSigKeyPair() != null) { byte[] pubKey = new ECKey().getPubKey(); - String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(user.getP2PSigPubKey()); + String p2pSigPubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(user.getP2PSigPubKey()); List languages = new ArrayList<>(); languages.add(LanguageUtil.getDefaultLanguageLocale()); List arbitrationMethods = new ArrayList<>(); @@ -166,7 +166,7 @@ class RestrictionsDataModel implements Activatable, DataModel { // TODO use very small sec. dposit to make testing in testnet less expensive // Revert later to 0.1 BTC again Arbitrator arbitrator = new Arbitrator(pubKey, - messagePubKeyAsHex, + p2pSigPubKeyAsHex, "Manfred Karrer", Arbitrator.ID_TYPE.REAL_LIFE_ID, languages, diff --git a/core/src/main/java/io/bitsquare/gui/main/debug/DebugView.java b/core/src/main/java/io/bitsquare/gui/main/debug/DebugView.java index 149594c8dc..1890e81c68 100644 --- a/core/src/main/java/io/bitsquare/gui/main/debug/DebugView.java +++ b/core/src/main/java/io/bitsquare/gui/main/debug/DebugView.java @@ -45,7 +45,7 @@ import io.bitsquare.trade.protocol.trade.offerer.tasks.VerifyTakerAccount; import io.bitsquare.trade.protocol.trade.taker.SellerAsTakerProtocol; import io.bitsquare.trade.protocol.trade.taker.tasks.CreateAndSignContract; import io.bitsquare.trade.protocol.trade.taker.tasks.CreateTakeOfferFeeTx; -import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessBankTransferStartedMessage; +import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessFiatTransferStartedMessage; import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessDepositTxPublishedMessage; import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessRequestDepositPaymentMessage; import io.bitsquare.trade.protocol.trade.taker.tasks.SendPayoutTxToOfferer; @@ -135,7 +135,7 @@ public class DebugView extends InitializableView { ProcessDepositTxPublishedMessage.class, TakerCommitDepositTx.class, - ProcessBankTransferStartedMessage.class, + ProcessFiatTransferStartedMessage.class, SignAndPublishPayoutTx.class, VerifyOfferFeePayment.class, diff --git a/core/src/main/java/io/bitsquare/gui/main/portfolio/closed/ClosedTradesDataModel.java b/core/src/main/java/io/bitsquare/gui/main/portfolio/closed/ClosedTradesDataModel.java index dd69559c58..0ef1d40bbf 100644 --- a/core/src/main/java/io/bitsquare/gui/main/portfolio/closed/ClosedTradesDataModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/portfolio/closed/ClosedTradesDataModel.java @@ -74,7 +74,7 @@ class ClosedTradesDataModel implements Activatable, DataModel { } public Direction getDirection(Offer offer) { - return offer.getMessagePublicKey().equals(user.getP2PSigPubKey()) ? + return offer.getP2PSigPubKey().equals(user.getP2PSigPubKey()) ? offer.getDirection() : offer.getMirroredDirection(); } diff --git a/core/src/main/java/io/bitsquare/gui/main/portfolio/offer/OffersDataModel.java b/core/src/main/java/io/bitsquare/gui/main/portfolio/offer/OffersDataModel.java index 6d3c29888e..72920dbb09 100644 --- a/core/src/main/java/io/bitsquare/gui/main/portfolio/offer/OffersDataModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/portfolio/offer/OffersDataModel.java @@ -86,7 +86,7 @@ class OffersDataModel implements Activatable, DataModel { } public Direction getDirection(Offer offer) { - return offer.getMessagePublicKey().equals(user.getP2PSigPubKey()) ? + return offer.getP2PSigPubKey().equals(user.getP2PSigPubKey()) ? offer.getDirection() : offer.getMirroredDirection(); } } diff --git a/core/src/main/java/io/bitsquare/gui/main/portfolio/pending/PendingTradesDataModel.java b/core/src/main/java/io/bitsquare/gui/main/portfolio/pending/PendingTradesDataModel.java index df1ec675cf..77adc381da 100644 --- a/core/src/main/java/io/bitsquare/gui/main/portfolio/pending/PendingTradesDataModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/portfolio/pending/PendingTradesDataModel.java @@ -146,7 +146,7 @@ class PendingTradesDataModel implements Activatable, DataModel { selectedItem = item; if (selectedItem != null) { - isOfferer = getTrade().getOffer().getMessagePublicKey().equals(user.getP2PSigPubKey()); + isOfferer = getTrade().getOffer().getP2PSigPubKey().equals(user.getP2PSigPubKey()); Trade trade = getTrade(); trade.stateProperty().addListener(tradeStateChangeListener); @@ -172,7 +172,6 @@ class PendingTradesDataModel implements Activatable, DataModel { } void fiatPaymentStarted() { - getTrade().setState(Trade.State.FIAT_PAYMENT_STARTED); tradeManager.onFiatPaymentStarted(getTrade().getId()); } @@ -209,7 +208,7 @@ class PendingTradesDataModel implements Activatable, DataModel { log.error(e.getMessage()); } - tradeManager.closeTrade(getTrade()); + tradeManager.onWithdrawAtTradeCompleted(getTrade()); /* Action response = Popups.openConfirmPopup( @@ -269,7 +268,7 @@ class PendingTradesDataModel implements Activatable, DataModel { } public Direction getDirection(Offer offer) { - return offer.getMessagePublicKey().equals(user.getP2PSigPubKey()) ? + return offer.getP2PSigPubKey().equals(user.getP2PSigPubKey()) ? offer.getDirection() : offer.getMirroredDirection(); } diff --git a/core/src/main/java/io/bitsquare/gui/main/trade/offerbook/OfferBookDataModel.java b/core/src/main/java/io/bitsquare/gui/main/trade/offerbook/OfferBookDataModel.java index 3c79b62237..4d0d4c5df4 100644 --- a/core/src/main/java/io/bitsquare/gui/main/trade/offerbook/OfferBookDataModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/trade/offerbook/OfferBookDataModel.java @@ -213,7 +213,7 @@ class OfferBookDataModel implements Activatable, DataModel { } boolean isMyOffer(Offer offer) { - return offer.getMessagePublicKey() != null && offer.getMessagePublicKey().equals(user.getP2PSigPubKey()); + return offer.getP2PSigPubKey() != null && offer.getP2PSigPubKey().equals(user.getP2PSigPubKey()); } Coin getAmountAsCoin() { diff --git a/core/src/main/java/io/bitsquare/gui/main/trade/takeoffer/TakeOfferDataModel.java b/core/src/main/java/io/bitsquare/gui/main/trade/takeoffer/TakeOfferDataModel.java index 0cc8da2ca0..896218cae4 100644 --- a/core/src/main/java/io/bitsquare/gui/main/trade/takeoffer/TakeOfferDataModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/trade/takeoffer/TakeOfferDataModel.java @@ -97,7 +97,7 @@ class TakeOfferDataModel implements Activatable, DataModel { @Override public void deactivate() { btcCode.unbind(); - tradeManager.cancelGetOfferAvailableStateRequest(offer); + tradeManager.cancelCheckOfferAvailabilityRequest(offer); } void initWithData(Coin amount, Offer offer) { diff --git a/core/src/main/java/io/bitsquare/offer/Offer.java b/core/src/main/java/io/bitsquare/offer/Offer.java index 2d46349733..4722be03a9 100644 --- a/core/src/main/java/io/bitsquare/offer/Offer.java +++ b/core/src/main/java/io/bitsquare/offer/Offer.java @@ -70,7 +70,7 @@ public class Offer implements Serializable { private final Coin amount; private final Coin minAmount; //TODO use hex string - private final PublicKey messagePublicKey; + private final PublicKey p2pSigPubKey; private final FiatAccountType fiatAccountType; private final Country bankAccountCountry; @@ -94,7 +94,7 @@ public class Offer implements Serializable { /////////////////////////////////////////////////////////////////////////////////////////// public Offer(String id, - PublicKey messagePublicKey, + PublicKey p2pSigPubKey, Direction direction, long fiatPrice, Coin amount, @@ -108,7 +108,7 @@ public class Offer implements Serializable { List acceptedCountries, List acceptedLanguageLocales) { this.id = id; - this.messagePublicKey = messagePublicKey; + this.p2pSigPubKey = p2pSigPubKey; this.direction = direction; this.fiatPrice = fiatPrice; this.amount = amount; @@ -221,8 +221,8 @@ public class Offer implements Serializable { return bankAccountUID; } - public PublicKey getMessagePublicKey() { - return messagePublicKey; + public PublicKey getP2PSigPubKey() { + return p2pSigPubKey; } public Date getCreationDate() { @@ -250,7 +250,7 @@ public class Offer implements Serializable { checkNotNull(getCurrency(), "Currency is null"); checkNotNull(getDirection(), "Direction is null"); checkNotNull(getId(), "Id is null"); - checkNotNull(getMessagePublicKey(), "MessagePublicKey is null"); + checkNotNull(getP2PSigPubKey(), "p2pSigPubKey is null"); checkNotNull(getMinAmount(), "MinAmount is null"); checkNotNull(getPrice(), "Price is null"); @@ -277,7 +277,7 @@ public class Offer implements Serializable { ", fiatPrice=" + fiatPrice + ", amount=" + amount + ", minAmount=" + minAmount + - ", messagePublicKey=" + messagePublicKey + + ", p2pSigPubKey=" + p2pSigPubKey + ", fiatAccountType=" + fiatAccountType + ", bankAccountCountry=" + bankAccountCountry + ", securityDeposit=" + securityDeposit + diff --git a/core/src/main/java/io/bitsquare/p2p/AddressService.java b/core/src/main/java/io/bitsquare/p2p/AddressService.java index 7c0f4808f4..5f31d83a1a 100644 --- a/core/src/main/java/io/bitsquare/p2p/AddressService.java +++ b/core/src/main/java/io/bitsquare/p2p/AddressService.java @@ -23,5 +23,5 @@ import io.bitsquare.p2p.listener.GetPeerAddressListener; import java.security.PublicKey; public interface AddressService extends DHTService{ - void findPeerAddress(PublicKey messagePublicKey, GetPeerAddressListener getPeerAddressListener); + void findPeerAddress(PublicKey p2pSigPubKey, GetPeerAddressListener getPeerAddressListener); } diff --git a/core/src/main/java/io/bitsquare/p2p/EncryptedMailboxMessage.java b/core/src/main/java/io/bitsquare/p2p/EncryptedMailboxMessage.java index 30759335ec..25e7205f7a 100644 --- a/core/src/main/java/io/bitsquare/p2p/EncryptedMailboxMessage.java +++ b/core/src/main/java/io/bitsquare/p2p/EncryptedMailboxMessage.java @@ -17,7 +17,7 @@ package io.bitsquare.p2p; -import io.bitsquare.crypto.EncryptionService; +import io.bitsquare.crypto.EncryptionPackage; import java.io.Serializable; @@ -31,13 +31,13 @@ public class EncryptedMailboxMessage implements MailboxMessage, Serializable { private static final long serialVersionUID = -3111178895546299769L; private static final Logger log = LoggerFactory.getLogger(EncryptedMailboxMessage.class); - private EncryptionService.Tuple tuple; + private EncryptionPackage encryptionPackage; - public EncryptedMailboxMessage(EncryptionService.Tuple tuple) { - this.tuple = tuple; + public EncryptedMailboxMessage(EncryptionPackage encryptionPackage) { + this.encryptionPackage = encryptionPackage; } - public EncryptionService.Tuple getTuple() { - return tuple; + public EncryptionPackage getEncryptionPackage() { + return encryptionPackage; } } diff --git a/core/src/main/java/io/bitsquare/p2p/MailboxMessagesResultHandler.java b/core/src/main/java/io/bitsquare/p2p/MailboxMessagesResultHandler.java new file mode 100644 index 0000000000..559343b2f1 --- /dev/null +++ b/core/src/main/java/io/bitsquare/p2p/MailboxMessagesResultHandler.java @@ -0,0 +1,24 @@ +/* + * 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 . + */ + +package io.bitsquare.p2p; + +import java.util.List; + +public interface MailboxMessagesResultHandler { + void handleResult(List messages); +} diff --git a/core/src/main/java/io/bitsquare/p2p/MailboxService.java b/core/src/main/java/io/bitsquare/p2p/MailboxService.java index ad5e1168c7..786a261b82 100644 --- a/core/src/main/java/io/bitsquare/p2p/MailboxService.java +++ b/core/src/main/java/io/bitsquare/p2p/MailboxService.java @@ -19,14 +19,14 @@ package io.bitsquare.p2p; import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.ResultHandler; -import io.bitsquare.p2p.tomp2p.TomP2PMailboxService; import java.security.PublicKey; public interface MailboxService { - void addMessage(PublicKey publicKey, EncryptedMailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler); + void addMessage(PublicKey p2pSigPubKey, EncryptedMailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler); + + void getAllMessages(PublicKey p2pSigPubKey, MailboxMessagesResultHandler resultHandler); - void removeMessage(PublicKey publicKey, EncryptedMailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler); + void removeAllMessages(PublicKey p2pSigPubKey, ResultHandler resultHandler, FaultHandler faultHandler); - void getMessages(PublicKey publicKey, TomP2PMailboxService.MailboxMessagesResultHandler resultHandler); } diff --git a/core/src/main/java/io/bitsquare/p2p/MessageService.java b/core/src/main/java/io/bitsquare/p2p/MessageService.java index 974aa658ad..26d1a7e9f4 100644 --- a/core/src/main/java/io/bitsquare/p2p/MessageService.java +++ b/core/src/main/java/io/bitsquare/p2p/MessageService.java @@ -20,10 +20,14 @@ package io.bitsquare.p2p; import io.bitsquare.p2p.listener.SendMessageListener; +import java.security.PublicKey; + public interface MessageService extends P2PService { void sendMessage(Peer peer, Message message, SendMessageListener listener); + void sendMessage(Peer peer, Message message, PublicKey p2pSigPubKey, PublicKey p2pEncryptPubKey, SendMessageListener listener); + void addMessageHandler(MessageHandler listener); void removeMessageHandler(MessageHandler listener); diff --git a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PAddressService.java b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PAddressService.java index e99a7919a1..03ec7ee212 100644 --- a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PAddressService.java +++ b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PAddressService.java @@ -91,9 +91,9 @@ public class TomP2PAddressService extends TomP2PDHTService implements AddressSer /////////////////////////////////////////////////////////////////////////////////////////// @Override - public void findPeerAddress(PublicKey publicKey, GetPeerAddressListener listener) { - final Number160 locationKey = Utils.makeSHAHash(publicKey.getEncoded()); - FutureGet futureGet = getDataOfProtectedDomain(locationKey, publicKey); + public void findPeerAddress(PublicKey p2pSigPubKey, GetPeerAddressListener listener) { + final Number160 locationKey = Utils.makeSHAHash(p2pSigPubKey.getEncoded()); + FutureGet futureGet = getDataOfProtectedDomain(locationKey, p2pSigPubKey); log.trace("findPeerAddress called"); futureGet.addListener(new BaseFutureAdapter() { @Override diff --git a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PDHTService.java b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PDHTService.java index 120424d23e..2f2652c2c3 100644 --- a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PDHTService.java +++ b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PDHTService.java @@ -247,6 +247,18 @@ public class TomP2PDHTService extends TomP2PService implements DHTService { log.trace("getDataFromMapOfMyProtectedDomain"); return peerDHT.get(locationKey).all().domainKey(pubKeyHashForMyDomain).start(); } + + /** + * Remove all data from map for given locationKey. + * Access: Only the domain owner. + * + * @param locationKey + * @return + */ + public FutureRemove removeAllDataFromMapOfMyProtectedDomain(Number160 locationKey) { + log.trace("getDataFromMapOfMyProtectedDomain"); + return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(keyPair).all().domainKey(pubKeyHashForMyDomain).keyPair(keyPair).start(); + } } diff --git a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMailboxService.java b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMailboxService.java index 4194e5b4d9..f973277b92 100644 --- a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMailboxService.java +++ b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMailboxService.java @@ -21,6 +21,7 @@ import io.bitsquare.common.handlers.FaultHandler; import io.bitsquare.common.handlers.ResultHandler; 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; @@ -49,7 +50,7 @@ import org.slf4j.LoggerFactory; public class TomP2PMailboxService extends TomP2PDHTService implements MailboxService { private static final Logger log = LoggerFactory.getLogger(TomP2PMailboxService.class); - private static final int TTL = 15 * 24 * 60 * 60; // the message is default 15 days valid, as a max trade period might be about 2 weeks. + 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 offerRepositoryListeners = new ArrayList<>(); @@ -69,14 +70,14 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer } @Override - public void addMessage(PublicKey publicKey, EncryptedMailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler) { + public void addMessage(PublicKey p2pSigPubKey, EncryptedMailboxMessage 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(publicKey) + + log.trace("Add message to DHT requested. Added data: [locationKey: " + getLocationKey(p2pSigPubKey) + ", hash: " + data.hash().toString() + "]"); - FuturePut futurePut = addDataToMapOfProtectedDomain(getLocationKey(publicKey), data, publicKey); + FuturePut futurePut = addDataToMapOfProtectedDomain(getLocationKey(p2pSigPubKey), data, p2pSigPubKey); futurePut.addListener(new BaseFutureListener() { @Override public void operationComplete(BaseFuture future) throws Exception { @@ -84,7 +85,7 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer executor.execute(() -> { resultHandler.handleResult(); - log.trace("Add message to mailbox was successful. Added data: [locationKey: " + getLocationKey(publicKey) + + log.trace("Add message to mailbox was successful. Added data: [locationKey: " + getLocationKey(p2pSigPubKey) + ", value: " + data + "]"); }); } @@ -101,41 +102,9 @@ public class TomP2PMailboxService extends TomP2PDHTService implements MailboxSer } @Override - public void removeMessage(PublicKey publicKey, EncryptedMailboxMessage message, ResultHandler resultHandler, FaultHandler faultHandler) { - try { - final Data data = new Data(message); - log.trace("Remove message from DHT requested. Removed data: [locationKey: " + getLocationKey(publicKey) + - ", hash: " + data.hash().toString() + "]"); - FutureRemove futureRemove = removeDataFromMapOfMyProtectedDomain(getLocationKey(publicKey), data); - futureRemove.addListener(new BaseFutureListener() { - @Override - public void operationComplete(BaseFuture future) throws Exception { - // We don't test futureRemove.isSuccess() as this API does not fit well to that operation, - // it might change in future to something like foundAndRemoved and notFound - // See discussion at: https://github.com/tomp2p/TomP2P/issues/57#issuecomment-62069840 - log.trace("isRemoved? " + futureRemove.isRemoved()); - executor.execute(() -> { - resultHandler.handleResult(); - }); - } - - @Override - public void exceptionCaught(Throwable t) throws Exception { - log.error("Remove message from DHT failed. Error: " + t.getMessage()); - faultHandler.handleFault("Remove message from DHT failed. Error: " + t.getMessage(), t); - } - }); - } catch (IOException e) { - e.printStackTrace(); - log.error("Remove message from DHT failed. Error: " + e.getMessage()); - faultHandler.handleFault("Remove message from DHT failed. Error: " + e.getMessage(), e); - } - } - - @Override - public void getMessages(PublicKey publicKey, MailboxMessagesResultHandler resultHandler) { - log.trace("Get messages from DHT requested for locationKey: " + getLocationKey(publicKey)); - FutureGet futureGet = getDataFromMapOfMyProtectedDomain(getLocationKey(publicKey)); + public void getAllMessages(PublicKey p2pSigPubKey, MailboxMessagesResultHandler resultHandler) { + log.trace("Get messages from DHT requested for locationKey: " + getLocationKey(p2pSigPubKey)); + FutureGet futureGet = getDataFromMapOfMyProtectedDomain(getLocationKey(p2pSigPubKey)); futureGet.addListener(new BaseFutureAdapter() { @Override public void operationComplete(BaseFuture future) throws Exception { @@ -156,7 +125,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(publicKey) + log.trace("Get messages from DHT was successful. Stored data: [key: " + getLocationKey(p2pSigPubKey) + ", values: " + futureGet.dataMap() + "]"); } else { @@ -173,16 +142,32 @@ 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)); + futureRemove.addListener(new BaseFutureListener() { + @Override + public void operationComplete(BaseFuture future) throws Exception { + // We don't test futureRemove.isSuccess() as this API does not fit well to that operation, + // it might change in future to something like foundAndRemoved and notFound + // See discussion at: https://github.com/tomp2p/TomP2P/issues/57#issuecomment-62069840 + log.trace("isRemoved? " + futureRemove.isRemoved()); + executor.execute(() -> { + resultHandler.handleResult(); + }); + } - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - private Number160 getLocationKey(PublicKey publicKey) { - return Number160.createHash("mailbox" + publicKey.hashCode()); + @Override + public void exceptionCaught(Throwable t) throws Exception { + log.error("Remove all messages from DHT failed. Error: " + t.getMessage()); + faultHandler.handleFault("Remove all messages from DHT failed. Error: " + t.getMessage(), t); + } + }); } - public interface MailboxMessagesResultHandler { - void handleResult(List messages); + + private Number160 getLocationKey(PublicKey p2pSigPubKey) { + return Number160.createHash("mailbox" + p2pSigPubKey.hashCode()); } } \ No newline at end of file diff --git a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMessageService.java b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMessageService.java index 094fe0a245..356c804125 100644 --- a/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMessageService.java +++ b/core/src/main/java/io/bitsquare/p2p/tomp2p/TomP2PMessageService.java @@ -17,6 +17,7 @@ package io.bitsquare.p2p.tomp2p; +import io.bitsquare.crypto.EncryptionPackage; import io.bitsquare.crypto.EncryptionService; import io.bitsquare.p2p.EncryptedMailboxMessage; import io.bitsquare.p2p.MailboxMessage; @@ -28,6 +29,8 @@ import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.listener.SendMessageListener; import io.bitsquare.user.User; +import java.security.PublicKey; + import java.util.concurrent.CopyOnWriteArrayList; import javax.inject.Inject; @@ -53,7 +56,7 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public TomP2PMessageService(TomP2PNode tomP2PNode, MailboxService mailboxService, User user, EncryptionService encryptionService) { + public TomP2PMessageService(TomP2PNode tomP2PNode, MailboxService mailboxService, User user, EncryptionService encryptionService) { super(tomP2PNode); this.mailboxService = mailboxService; this.user = user; @@ -74,6 +77,12 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic @Override public void sendMessage(Peer peer, Message message, SendMessageListener listener) { + sendMessage(peer, message, null, null, listener); + } + + @Override + public void sendMessage(Peer peer, Message message, PublicKey p2pSigPubKey, PublicKey p2pEncryptPubKey, + SendMessageListener listener) { if (!(peer instanceof TomP2PPeer)) throw new IllegalArgumentException("Peer must be of type TomP2PPeer"); @@ -86,8 +95,9 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic executor.execute(listener::handleResult); } else { - if (message instanceof MailboxMessage) { - sendMailboxMessage((MailboxMessage) message, listener); + if (p2pSigPubKey != null && p2pEncryptPubKey != null) { + log.info("sendMessage failed. We will try to send the message to the mailbox. Fault reason: " + futureDirect.failedReason()); + sendMailboxMessage(p2pSigPubKey, p2pEncryptPubKey, (MailboxMessage) message, listener); } else { log.error("sendMessage failed with reason " + futureDirect.failedReason()); @@ -98,8 +108,9 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic @Override public void exceptionCaught(Throwable t) throws Exception { - if (message instanceof MailboxMessage) { - sendMailboxMessage((MailboxMessage) message, listener); + if (p2pSigPubKey != null && p2pEncryptPubKey != null) { + log.info("sendMessage failed with exception. We will try to send the message to the mailbox. Exception: " + t.getMessage()); + sendMailboxMessage(p2pSigPubKey, p2pEncryptPubKey, (MailboxMessage) message, listener); } else { log.error("sendMessage failed with exception " + t.getMessage()); @@ -109,11 +120,18 @@ public class TomP2PMessageService extends TomP2PService implements MessageServic }); } - private void sendMailboxMessage(MailboxMessage message, SendMessageListener listener) { - EncryptionService.Tuple tuple = encryptionService.encryptObject(user.getP2pEncryptKeyPair().getPublic(), message); - - EncryptedMailboxMessage encrypted = new EncryptedMailboxMessage(tuple); - mailboxService.addMessage(user.getP2PSigPubKey(), encrypted, + private void sendMailboxMessage(PublicKey p2pSigPubKey, PublicKey p2pEncryptPubKey, MailboxMessage message, SendMessageListener listener) { + EncryptionPackage encryptionPackage = null; + try { + encryptionPackage = encryptionService.encryptObject(p2pEncryptPubKey, message); + } catch (Throwable t) { + t.printStackTrace(); + log.error(t.getMessage()); + executor.execute(listener::handleFault); + } + EncryptedMailboxMessage encrypted = new EncryptedMailboxMessage(encryptionPackage); + mailboxService.addMessage(p2pSigPubKey, + encrypted, () -> { log.debug("Message successfully added to peers mailbox."); executor.execute(listener::handleResult); diff --git a/core/src/main/java/io/bitsquare/trade/Contract.java b/core/src/main/java/io/bitsquare/trade/Contract.java index 1aa22b58c6..a79d814dd7 100644 --- a/core/src/main/java/io/bitsquare/trade/Contract.java +++ b/core/src/main/java/io/bitsquare/trade/Contract.java @@ -40,8 +40,8 @@ public class Contract implements Serializable { private final String takerAccountID; private final FiatAccount offererFiatAccount; private final FiatAccount takerFiatAccount; - private final String offererMessagePublicKeyAsString; - private final String takerMessagePublicKeyAsString; + private final String offererP2PSigPubKeyAsString; + private final String takerP2PSigPubKeyAsString; public Contract(Offer offer, Coin tradeAmount, @@ -50,8 +50,8 @@ public class Contract implements Serializable { String takerAccountID, FiatAccount offererFiatAccount, FiatAccount takerFiatAccount, - PublicKey offererMessagePublicKey, - PublicKey takerMessagePublicKey) { + PublicKey offererP2PSigPubKey, + PublicKey takerP2PSigPubKey) { this.offer = offer; this.tradeAmount = tradeAmount; this.takeOfferFeeTxID = takeOfferFeeTxID; @@ -59,8 +59,8 @@ public class Contract implements Serializable { this.takerAccountID = takerAccountID; this.offererFiatAccount = offererFiatAccount; this.takerFiatAccount = takerFiatAccount; - this.offererMessagePublicKeyAsString = DSAKeyUtil.getHexStringFromPublicKey(offererMessagePublicKey); - this.takerMessagePublicKeyAsString = DSAKeyUtil.getHexStringFromPublicKey(takerMessagePublicKey); + this.offererP2PSigPubKeyAsString = DSAKeyUtil.getHexStringFromPublicKey(offererP2PSigPubKey); + this.takerP2PSigPubKeyAsString = DSAKeyUtil.getHexStringFromPublicKey(takerP2PSigPubKey); } @@ -97,11 +97,11 @@ public class Contract implements Serializable { } public String getTakerMessagePublicKey() { - return takerMessagePublicKeyAsString; + return takerP2PSigPubKeyAsString; } public String getOffererMessagePublicKey() { - return offererMessagePublicKeyAsString; + return offererP2PSigPubKeyAsString; } @Override @@ -114,8 +114,8 @@ public class Contract implements Serializable { ", takerAccountID='" + takerAccountID + '\'' + ", offererBankAccount=" + offererFiatAccount + ", takerBankAccount=" + takerFiatAccount + - ", takerMessagePublicKeyAsString=" + takerMessagePublicKeyAsString + - ", offererMessagePublicKeyAsString=" + offererMessagePublicKeyAsString + + ", takerP2PSigPubKeyAsString=" + takerP2PSigPubKeyAsString + + ", offererP2PSigPubKeyAsString=" + offererP2PSigPubKeyAsString + '}'; } } diff --git a/core/src/main/java/io/bitsquare/trade/TradeManager.java b/core/src/main/java/io/bitsquare/trade/TradeManager.java index 6bd05aad06..95c6a1497c 100644 --- a/core/src/main/java/io/bitsquare/trade/TradeManager.java +++ b/core/src/main/java/io/bitsquare/trade/TradeManager.java @@ -21,12 +21,15 @@ import io.bitsquare.btc.BlockChainService; import io.bitsquare.btc.WalletService; import io.bitsquare.common.handlers.ErrorMessageHandler; import io.bitsquare.common.handlers.ResultHandler; +import io.bitsquare.crypto.EncryptionService; import io.bitsquare.crypto.SignatureService; import io.bitsquare.fiat.FiatAccount; import io.bitsquare.offer.Direction; 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; @@ -41,6 +44,7 @@ import io.bitsquare.trade.protocol.availability.messages.ReportOfferAvailability import io.bitsquare.trade.protocol.availability.messages.RequestIsOfferAvailableMessage; import io.bitsquare.trade.protocol.placeoffer.PlaceOfferModel; import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol; +import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.offerer.BuyerAsOffererProtocol; import io.bitsquare.trade.protocol.trade.offerer.models.BuyerAsOffererModel; import io.bitsquare.trade.protocol.trade.taker.SellerAsTakerProtocol; @@ -54,6 +58,7 @@ import org.bitcoinj.utils.Fiat; import java.io.Serializable; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.inject.Inject; @@ -78,6 +83,7 @@ public class TradeManager { private final BlockChainService blockChainService; private final WalletService walletService; private final SignatureService signatureService; + private EncryptionService encryptionService; private final OfferBookService offerBookService; private final Map sellerAsTakerProtocolMap = new HashMap<>(); @@ -87,6 +93,7 @@ public class TradeManager { private final ObservableMap openOffers = FXCollections.observableHashMap(); private final ObservableMap pendingTrades = FXCollections.observableHashMap(); private final ObservableMap closedTrades = FXCollections.observableHashMap(); + private final Map mailboxMessages = new HashMap<>(); private Trade currentPendingTrade; @@ -98,7 +105,7 @@ public class TradeManager { @Inject public TradeManager(User user, AccountSettings accountSettings, Persistence persistence, MessageService messageService, MailboxService mailboxService, AddressService addressService, BlockChainService blockChainService, - WalletService walletService, SignatureService signatureService, + WalletService walletService, SignatureService signatureService, EncryptionService encryptionService, OfferBookService offerBookService) { this.user = user; this.accountSettings = accountSettings; @@ -109,6 +116,7 @@ public class TradeManager { this.blockChainService = blockChainService; this.walletService = walletService; this.signatureService = signatureService; + this.encryptionService = encryptionService; this.offerBookService = offerBookService; Serializable openOffersObject = persistence.read(this, "openOffers"); @@ -125,10 +133,13 @@ public class TradeManager { if (closedTradesObject instanceof Map) { closedTrades.putAll((Map) closedTradesObject); } - - messageService.addMessageHandler(this::handleMessage); } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Public API + /////////////////////////////////////////////////////////////////////////////////////////// + // When all services are initialized we create the protocols for our open offers and persisted not completed pendingTrades // BuyerAcceptsOfferProtocol listens for take offer requests, so we need to instantiate it early. public void onAllServicesInitialized() { @@ -161,41 +172,25 @@ public class TradeManager { } } } + + mailboxService.getAllMessages(user.getP2PSigPubKey(), + (messages) -> { + decryptMailboxMessages(messages); + emptyMailbox(); + }); + + messageService.addMessageHandler(this::handleMessage); } public boolean isMyOffer(Offer offer) { - return offer.getMessagePublicKey().equals(user.getP2PSigPubKey()); + return offer.getP2PSigPubKey().equals(user.getP2PSigPubKey()); } /////////////////////////////////////////////////////////////////////////////////////////// - // Called from UI + // Offer /////////////////////////////////////////////////////////////////////////////////////////// - public void checkOfferAvailability(Offer offer) { - offer.setState(Offer.State.UNKNOWN); - if (!checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) { - CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel( - offer, - messageService, - addressService); - - CheckOfferAvailabilityProtocol protocol = new CheckOfferAvailabilityProtocol(model, - () -> disposeCheckOfferAvailabilityRequest(offer), - (errorMessage) -> disposeCheckOfferAvailabilityRequest(offer)); - checkOfferAvailabilityProtocolMap.put(offer.getId(), protocol); - protocol.checkOfferAvailability(); - } - else { - log.error("That should never happen: onCheckOfferAvailability already called for offer with ID:" + offer.getId()); - } - } - - // When closing take offer view, we are not interested in the onCheckOfferAvailability result anymore, so remove from the map - public void cancelGetOfferAvailableStateRequest(Offer offer) { - disposeCheckOfferAvailabilityRequest(offer); - } - public void placeOffer(String id, Direction direction, Fiat price, @@ -240,6 +235,35 @@ public class TradeManager { removeOpenOffer(offer, resultHandler, errorMessageHandler, true); } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Take offer + /////////////////////////////////////////////////////////////////////////////////////////// + + public void checkOfferAvailability(Offer offer) { + offer.setState(Offer.State.UNKNOWN); + if (!checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) { + CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel( + offer, + messageService, + addressService); + + CheckOfferAvailabilityProtocol protocol = new CheckOfferAvailabilityProtocol(model, + () -> disposeCheckOfferAvailabilityRequest(offer), + (errorMessage) -> disposeCheckOfferAvailabilityRequest(offer)); + checkOfferAvailabilityProtocolMap.put(offer.getId(), protocol); + protocol.checkOfferAvailability(); + } + else { + log.error("That should never happen: onCheckOfferAvailability already called for offer with ID:" + offer.getId()); + } + } + + // When closing take offer view, we are not interested in the onCheckOfferAvailability result anymore, so remove from the map + public void cancelCheckOfferAvailabilityRequest(Offer offer) { + disposeCheckOfferAvailabilityRequest(offer); + } + public void requestTakeOffer(Coin amount, Offer offer, TradeResultHandler tradeResultHandler) { CheckOfferAvailabilityModel model = new CheckOfferAvailabilityModel(offer, messageService, addressService); CheckOfferAvailabilityProtocol protocol = new CheckOfferAvailabilityProtocol(model, @@ -255,6 +279,138 @@ public class TradeManager { protocol.checkOfferAvailability(); } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Trade + /////////////////////////////////////////////////////////////////////////////////////////// + + public void onFiatPaymentStarted(String tradeId) { + // TODO remove if check when persistence is impl. + if (buyerAcceptsOfferProtocolMap.containsKey(tradeId)) { + buyerAcceptsOfferProtocolMap.get(tradeId).onFiatPaymentStarted(); + persistPendingTrades(); + } + } + + public void onFiatPaymentReceived(String tradeId) { + sellerAsTakerProtocolMap.get(tradeId).onFiatPaymentReceived(); + } + + public void onWithdrawAtTradeCompleted(Trade trade) { + closeTrade(trade); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Called from Offerbook when offer gets removed from DHT + /////////////////////////////////////////////////////////////////////////////////////////// + + public void onOfferRemovedFromRemoteOfferBook(Offer offer) { + disposeCheckOfferAvailabilityRequest(offer); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Message handler + /////////////////////////////////////////////////////////////////////////////////////////// + + // Offerer handles those requests + private void handleMessage(Message message, Peer sender) { + if (message instanceof RequestIsOfferAvailableMessage) { + String offerId = ((RequestIsOfferAvailableMessage) message).offerId; + checkNotNull(offerId); + + ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(offerId, isOfferOpen(offerId)); + messageService.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 nto start a trade. + log.trace("ReportOfferAvailabilityMessage successfully arrived at peer"); + } + + @Override + public void handleFault() { + log.warn("Sending ReportOfferAvailabilityMessage failed."); + } + }); + } + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Getters + /////////////////////////////////////////////////////////////////////////////////////////// + + public ObservableMap getOpenOffers() { + return openOffers; + } + + public ObservableMap getPendingTrades() { + return pendingTrades; + } + + public ObservableMap getClosedTrades() { + return closedTrades; + } + + public Trade getCurrentPendingTrade() { + return currentPendingTrade; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private methods + /////////////////////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////////////////////// + // Offer + /////////////////////////////////////////////////////////////////////////////////////////// + + private void removeOpenOffer(Offer offer, + ResultHandler resultHandler, + ErrorMessageHandler errorMessageHandler, + boolean removeFromBuyerAcceptsOfferProtocolMap) { + String offerId = offer.getId(); + offerBookService.removeOffer(offer, + () -> { + if (openOffers.containsKey(offerId)) { + openOffers.remove(offerId); + disposeCheckOfferAvailabilityRequest(offer); + persistOpenOffers(); + if (removeFromBuyerAcceptsOfferProtocolMap && buyerAcceptsOfferProtocolMap.containsKey(offerId)) { + buyerAcceptsOfferProtocolMap.get(offerId).cleanup(); + buyerAcceptsOfferProtocolMap.remove(offerId); + } + + resultHandler.handleResult(); + } + else { + log.error("Locally stored offers does not contain the offer with the ID " + offerId); + errorMessageHandler.handleErrorMessage("Locally stored offers does not contain the offer with the ID " + offerId); + } + }, + (message, throwable) -> errorMessageHandler.handleErrorMessage(message)); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Take offer + /////////////////////////////////////////////////////////////////////////////////////////// + + private void disposeCheckOfferAvailabilityRequest(Offer offer) { + if (checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) { + CheckOfferAvailabilityProtocol protocol = checkOfferAvailabilityProtocolMap.get(offer.getId()); + protocol.cancel(); + protocol.cleanup(); + checkOfferAvailabilityProtocolMap.remove(offer.getId()); + } + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Trade + /////////////////////////////////////////////////////////////////////////////////////////// + private Trade takeAvailableOffer(Coin amount, Offer offer, Peer peer) { Trade trade = createTrade(offer); trade.setTradeAmount(amount); @@ -264,6 +420,19 @@ public class TradeManager { return trade; } + private Trade createTrade(Offer offer) { + if (pendingTrades.containsKey(offer.getId())) + log.error("That must never happen: Trades contains already an trade with the ID " + offer.getId()); + + Trade trade = new Trade(offer); + pendingTrades.put(offer.getId(), trade); + persistPendingTrades(); + + currentPendingTrade = trade; + + return trade; + } + private SellerAsTakerProtocol createSellerAsTakerProtocol(Trade trade, Peer peer) { trade.stateProperty().addListener((ov, oldValue, newValue) -> { log.debug("trade state = " + newValue); @@ -292,6 +461,7 @@ public class TradeManager { trade, peer, messageService, + mailboxService, walletService, blockChainService, signatureService, @@ -300,122 +470,13 @@ public class TradeManager { SellerAsTakerProtocol sellerTakesOfferProtocol = new SellerAsTakerProtocol(model); sellerAsTakerProtocolMap.put(trade.getId(), sellerTakesOfferProtocol); + + if (mailboxMessages.containsKey(trade.getId())) + sellerTakesOfferProtocol.setMailboxMessage(mailboxMessages.get(trade.getId())); + return sellerTakesOfferProtocol; } - public void onFiatPaymentStarted(String tradeId) { - // TODO remove if check when persistence is impl. - if (buyerAcceptsOfferProtocolMap.containsKey(tradeId)) { - buyerAcceptsOfferProtocolMap.get(tradeId).onFiatPaymentStarted(); - persistPendingTrades(); - } - } - - public void onFiatPaymentReceived(String tradeId) { - sellerAsTakerProtocolMap.get(tradeId).onFiatPaymentReceived(); - } - - public void closeTrade(Trade trade) { - if (pendingTrades.containsKey(trade.getId())) { - pendingTrades.remove(trade.getId()); - persistPendingTrades(); - } - - if (sellerAsTakerProtocolMap.containsKey(trade.getId())) { - sellerAsTakerProtocolMap.get(trade.getId()).cleanup(); - sellerAsTakerProtocolMap.remove(trade.getId()); - } - else if (buyerAcceptsOfferProtocolMap.containsKey(trade.getId())) { - buyerAcceptsOfferProtocolMap.get(trade.getId()).cleanup(); - buyerAcceptsOfferProtocolMap.remove(trade.getId()); - } - - if (!closedTrades.containsKey(trade.getId())) { - closedTrades.put(trade.getId(), trade); - persistClosedTrades(); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Called from Offerbook when offer gets removed from DHT - /////////////////////////////////////////////////////////////////////////////////////////// - - public void onOfferRemovedFromRemoteOfferBook(Offer offer) { - disposeCheckOfferAvailabilityRequest(offer); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Process new tradeMessages - /////////////////////////////////////////////////////////////////////////////////////////// - - // Offerer handles those requests - private void handleMessage(Message message, Peer sender) { - if (message instanceof RequestIsOfferAvailableMessage) { - String offerId = ((RequestIsOfferAvailableMessage) message).offerId; - checkNotNull(offerId); - - ReportOfferAvailabilityMessage reportOfferAvailabilityMessage = new ReportOfferAvailabilityMessage(offerId, isOfferOpen(offerId)); - messageService.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 nto start a trade. - log.trace("ReportOfferAvailabilityMessage successfully arrived at peer"); - } - - @Override - public void handleFault() { - log.warn("Sending ReportOfferAvailabilityMessage failed."); - } - }); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - - - private void removeOpenOffer(Offer offer, - ResultHandler resultHandler, - ErrorMessageHandler errorMessageHandler, - boolean removeFromBuyerAcceptsOfferProtocolMap) { - String offerId = offer.getId(); - offerBookService.removeOffer(offer, - () -> { - if (openOffers.containsKey(offerId)) { - openOffers.remove(offerId); - disposeCheckOfferAvailabilityRequest(offer); - persistOpenOffers(); - if (removeFromBuyerAcceptsOfferProtocolMap && buyerAcceptsOfferProtocolMap.containsKey(offerId)) { - buyerAcceptsOfferProtocolMap.get(offerId).cleanup(); - buyerAcceptsOfferProtocolMap.remove(offerId); - } - - resultHandler.handleResult(); - } - else { - log.error("Locally stored offers does not contain the offer with the ID " + offerId); - errorMessageHandler.handleErrorMessage("Locally stored offers does not contain the offer with the ID " + offerId); - } - }, - (message, throwable) -> errorMessageHandler.handleErrorMessage(message)); - } - - private Trade createTrade(Offer offer) { - if (pendingTrades.containsKey(offer.getId())) - log.error("That must never happen: Trades contains already an trade with the ID " + offer.getId()); - - Trade trade = new Trade(offer); - pendingTrades.put(offer.getId(), trade); - persistPendingTrades(); - - currentPendingTrade = trade; - - return trade; - } private void createBuyerAcceptsOfferProtocol(Offer offer) { Trade trade; @@ -482,13 +543,63 @@ public class TradeManager { buyerAcceptsOfferProtocolMap.put(offer.getId(), buyerAcceptsOfferProtocol); } - private void disposeCheckOfferAvailabilityRequest(Offer offer) { - if (checkOfferAvailabilityProtocolMap.containsKey(offer.getId())) { - CheckOfferAvailabilityProtocol protocol = checkOfferAvailabilityProtocolMap.get(offer.getId()); - protocol.cancel(); - protocol.cleanup(); - checkOfferAvailabilityProtocolMap.remove(offer.getId()); + private void closeTrade(Trade trade) { + if (pendingTrades.containsKey(trade.getId())) { + pendingTrades.remove(trade.getId()); + persistPendingTrades(); } + + if (sellerAsTakerProtocolMap.containsKey(trade.getId())) { + sellerAsTakerProtocolMap.get(trade.getId()).cleanup(); + sellerAsTakerProtocolMap.remove(trade.getId()); + } + else if (buyerAcceptsOfferProtocolMap.containsKey(trade.getId())) { + buyerAcceptsOfferProtocolMap.get(trade.getId()).cleanup(); + buyerAcceptsOfferProtocolMap.remove(trade.getId()); + } + + if (!closedTrades.containsKey(trade.getId())) { + closedTrades.put(trade.getId(), trade); + persistClosedTrades(); + } + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Mailbox + /////////////////////////////////////////////////////////////////////////////////////////// + + private void decryptMailboxMessages(List encryptedMailboxMessages) { + log.trace("applyMailboxMessage encryptedMailboxMessage.size=" + encryptedMailboxMessages.size()); + for (EncryptedMailboxMessage encrypted : encryptedMailboxMessages) { + MailboxMessage mailboxMessage = null; + try { + mailboxMessage = encryptionService.decryptToObject(user.getP2pEncryptPrivateKey(), encrypted.getEncryptionPackage()); + } catch (Throwable e) { + e.printStackTrace(); + log.error(e.getMessage()); + } + if (mailboxMessage instanceof TradeMessage) { + String tradeId = ((TradeMessage) mailboxMessage).tradeId; + mailboxMessages.put(tradeId, mailboxMessage); + log.trace("mailboxMessage with tradeID " + tradeId); + if (sellerAsTakerProtocolMap.containsKey(tradeId)) { + sellerAsTakerProtocolMap.get(tradeId).setMailboxMessage(encrypted); + log.trace("sellerAsTakerProtocolMap exist with tradeID " + tradeId); + } + } + } + } + + private void emptyMailbox() { + mailboxService.removeAllMessages(user.getP2PSigPubKey(), + () -> { + log.debug("All mailbox entries removed"); + }, + (errorMessage, fault) -> { + log.error(errorMessage); + log.error(fault.getMessage()); + }); } boolean isOfferOpen(String offerId) { @@ -498,31 +609,6 @@ public class TradeManager { || openOffers.get(offerId).getState() == Offer.State.AVAILABLE); } - /////////////////////////////////////////////////////////////////////////////////////////// - // Getters - /////////////////////////////////////////////////////////////////////////////////////////// - - public ObservableMap getOpenOffers() { - return openOffers; - } - - public ObservableMap getPendingTrades() { - return pendingTrades; - } - - public ObservableMap getClosedTrades() { - return closedTrades; - } - - public Trade getCurrentPendingTrade() { - return currentPendingTrade; - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Private - /////////////////////////////////////////////////////////////////////////////////////////// - private void persistOpenOffers() { persistence.write(this, "openOffers", (Map) new HashMap<>(openOffers)); } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/GetPeerAddress.java b/core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/GetPeerAddress.java index 3ca085e883..c3aae526b9 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/GetPeerAddress.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/availability/tasks/GetPeerAddress.java @@ -39,7 +39,7 @@ public class GetPeerAddress extends Task { @Override protected void doRun() { try { - model.addressService.findPeerAddress(model.offer.getMessagePublicKey(), new GetPeerAddressListener() { + model.addressService.findPeerAddress(model.offer.getP2PSigPubKey(), new GetPeerAddressListener() { @Override public void onResult(Peer peer) { model.setPeer(peer); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/SharedTradeModel.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/SharedTradeModel.java index bf37de7b65..5f7d54eb96 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/SharedTradeModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/SharedTradeModel.java @@ -22,8 +22,10 @@ import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; import io.bitsquare.common.taskrunner.SharedTaskModel; import io.bitsquare.crypto.SignatureService; -import io.bitsquare.p2p.MessageService; import io.bitsquare.offer.Offer; +import io.bitsquare.p2p.MailboxMessage; +import io.bitsquare.p2p.MailboxService; +import io.bitsquare.p2p.MessageService; import io.bitsquare.persistence.Persistence; import io.bitsquare.trade.protocol.trade.messages.TradeMessage; @@ -39,11 +41,14 @@ public class SharedTradeModel extends SharedTaskModel implements Serializable { // provided transient public final Offer offer; transient public final MessageService messageService; + transient public final MailboxService mailboxService; transient public final WalletService walletService; transient public final BlockChainService blockChainService; transient public final SignatureService signatureService; transient protected final Persistence persistence; + transient public MailboxMessage mailboxMessage; + // derived transient public final String id; transient public final TradeWalletService tradeWalletService; @@ -54,12 +59,14 @@ public class SharedTradeModel extends SharedTaskModel implements Serializable { protected SharedTradeModel(Offer offer, MessageService messageService, + MailboxService mailboxService, WalletService walletService, BlockChainService blockChainService, SignatureService signatureService, Persistence persistence) { this.offer = offer; this.messageService = messageService; + this.mailboxService = mailboxService; this.walletService = walletService; this.blockChainService = blockChainService; this.signatureService = signatureService; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/BankTransferStartedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/FiatTransferStartedMessage.java similarity index 93% rename from core/src/main/java/io/bitsquare/trade/protocol/trade/messages/BankTransferStartedMessage.java rename to core/src/main/java/io/bitsquare/trade/protocol/trade/messages/FiatTransferStartedMessage.java index fc4e0b9bb3..d4b6928fc2 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/BankTransferStartedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/FiatTransferStartedMessage.java @@ -23,7 +23,7 @@ import org.bitcoinj.core.Coin; import java.io.Serializable; -public class BankTransferStartedMessage extends TradeMessage implements MailboxMessage, Serializable { +public class FiatTransferStartedMessage extends TradeMessage implements MailboxMessage, Serializable { private static final long serialVersionUID = -3479634129543632523L; public final byte[] offererSignature; @@ -31,7 +31,7 @@ public class BankTransferStartedMessage extends TradeMessage implements MailboxM public final Coin takerPayoutAmount; public final String offererPayoutAddress; - public BankTransferStartedMessage(String tradeId, + public FiatTransferStartedMessage(String tradeId, byte[] offererSignature, Coin offererPayoutAmount, Coin takerPayoutAmount, diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestOffererPublishDepositTxMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestOffererPublishDepositTxMessage.java index b03cb5be05..82e74f6171 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestOffererPublishDepositTxMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/RequestOffererPublishDepositTxMessage.java @@ -33,7 +33,8 @@ public class RequestOffererPublishDepositTxMessage extends TradeMessage implemen public final FiatAccount takerFiatAccount; public final String takerAccountId; - public final PublicKey takerMessagePublicKey; + public final PublicKey takerP2PSigPublicKey; + public final PublicKey takerP2PEncryptPublicKey; public final String takerContractAsJson; public final String takerContractSignature; public final String takerPayoutAddressString; @@ -44,7 +45,8 @@ public class RequestOffererPublishDepositTxMessage extends TradeMessage implemen public RequestOffererPublishDepositTxMessage(String tradeId, FiatAccount takerFiatAccount, String takerAccountId, - PublicKey takerMessagePublicKey, + PublicKey takerP2PSigPublicKey, + PublicKey takerP2PEncryptPublicKey, String takerContractAsJson, String takerContractSignature, String takerPayoutAddressString, @@ -54,7 +56,8 @@ public class RequestOffererPublishDepositTxMessage extends TradeMessage implemen this.tradeId = tradeId; this.takerFiatAccount = takerFiatAccount; this.takerAccountId = takerAccountId; - this.takerMessagePublicKey = takerMessagePublicKey; + this.takerP2PSigPublicKey = takerP2PSigPublicKey; + this.takerP2PEncryptPublicKey = takerP2PEncryptPublicKey; this.takerContractAsJson = takerContractAsJson; this.takerContractSignature = takerContractSignature; this.takerPayoutAddressString = takerPayoutAddressString; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/models/BuyerAsOffererModel.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/models/BuyerAsOffererModel.java index e7c138dbaf..535cf892cc 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/models/BuyerAsOffererModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/models/BuyerAsOffererModel.java @@ -37,7 +37,6 @@ public class BuyerAsOffererModel extends SharedTradeModel implements Serializabl private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererModel.class); transient public final Trade trade; - transient public final MailboxService mailboxService; public final TakerModel taker; public final OffererModel offerer; @@ -54,13 +53,13 @@ public class BuyerAsOffererModel extends SharedTradeModel implements Serializabl Persistence persistence) { super(trade.getOffer(), messageService, + mailboxService, walletService, blockChainService, signatureService, persistence); this.trade = trade; - this.mailboxService = mailboxService; Serializable serializable = persistence.read(this, "BuyerAsOffererModel_" + id); if (serializable instanceof BuyerAsOffererModel) { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/models/TakerModel.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/models/TakerModel.java index c8de735e2f..7aea093dcb 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/models/TakerModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/models/TakerModel.java @@ -38,6 +38,7 @@ public class TakerModel implements Serializable { public String accountId; public FiatAccount fiatAccount; public PublicKey p2pSigPublicKey; + public PublicKey p2pEncryptPubKey; public String contractAsJson;//TODO only write access now, missing impl. public String contractSignature; public Coin payoutAmount; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessRequestOffererPublishDepositTxMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessRequestOffererPublishDepositTxMessage.java index 68055e08e4..e8de854d1f 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessRequestOffererPublishDepositTxMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessRequestOffererPublishDepositTxMessage.java @@ -43,7 +43,8 @@ public class ProcessRequestOffererPublishDepositTxMessage extends Task { @Override protected void doRun() { try { - BankTransferStartedMessage tradeMessage = new BankTransferStartedMessage( - model.id, + FiatTransferStartedMessage tradeMessage = new FiatTransferStartedMessage(model.id, model.offerer.payoutTxSignature, model.offerer.payoutAmount, model.taker.payoutAmount, model.offerer.addressEntry.getAddressString()); - model.messageService.sendMessage(model.taker.peer, tradeMessage, new SendMessageListener() { - @Override - public void handleResult() { - log.trace("Sending BankTransferInitedMessage succeeded."); - complete(); - } - @Override - public void handleFault() { - failed("Sending BankTransferInitedMessage failed."); - } - }); + model.messageService.sendMessage(model.taker.peer, tradeMessage, + model.taker.p2pSigPublicKey, + model.taker.p2pEncryptPubKey, + new SendMessageListener() { + @Override + public void handleResult() { + log.trace("Sending BankTransferInitedMessage succeeded."); + model.trade.setState(Trade.State.FIAT_PAYMENT_STARTED); + complete(); + } + + @Override + public void handleFault() { + failed("Sending BankTransferInitedMessage failed."); + model.trade.setState(Trade.State.FAULT); + } + }); } catch (Throwable t) { failed("Sending BankTransferInitedMessage failed."); + model.trade.setState(Trade.State.FAULT); } } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/SellerAsTakerProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/SellerAsTakerProtocol.java index 6fe8bd8728..ef02fa9fb1 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/SellerAsTakerProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/SellerAsTakerProtocol.java @@ -18,20 +18,21 @@ package io.bitsquare.trade.protocol.trade.taker; import io.bitsquare.common.taskrunner.TaskRunner; +import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.MessageHandler; import io.bitsquare.p2p.Peer; import io.bitsquare.trade.Trade; -import io.bitsquare.trade.protocol.trade.messages.BankTransferStartedMessage; import io.bitsquare.trade.protocol.trade.messages.DepositTxPublishedMessage; +import io.bitsquare.trade.protocol.trade.messages.FiatTransferStartedMessage; import io.bitsquare.trade.protocol.trade.messages.RequestDepositPaymentMessage; import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.taker.models.SellerAsTakerModel; import io.bitsquare.trade.protocol.trade.taker.tasks.BroadcastTakeOfferFeeTx; import io.bitsquare.trade.protocol.trade.taker.tasks.CreateAndSignContract; import io.bitsquare.trade.protocol.trade.taker.tasks.CreateTakeOfferFeeTx; -import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessBankTransferStartedMessage; import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessDepositTxPublishedMessage; +import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessFiatTransferStartedMessage; import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessRequestDepositPaymentMessage; import io.bitsquare.trade.protocol.trade.taker.tasks.SendPayoutTxToOfferer; import io.bitsquare.trade.protocol.trade.taker.tasks.SendRequestDepositTxInputsMessage; @@ -70,6 +71,8 @@ public class SellerAsTakerProtocol { this.model = model; messageHandler = this::handleMessage; model.messageService.addMessageHandler(messageHandler); + + } @@ -82,6 +85,17 @@ public class SellerAsTakerProtocol { model.messageService.removeMessageHandler(messageHandler); } + public void setMailboxMessage(MailboxMessage mailboxMessage) { + log.debug("setMailboxMessage " + mailboxMessage); + // Might be called twice, so check that its only processed once + if (model.mailboxMessage == null) { + model.mailboxMessage = mailboxMessage; + if (mailboxMessage instanceof FiatTransferStartedMessage) { + handleFiatTransferStartedMessage((FiatTransferStartedMessage) mailboxMessage); + } + } + } + public void takeAvailableOffer() { TaskRunner taskRunner = new TaskRunner<>(model, () -> { @@ -99,7 +113,7 @@ public class SellerAsTakerProtocol { taskRunner.run(); } - + /////////////////////////////////////////////////////////////////////////////////////////// // Incoming message handling /////////////////////////////////////////////////////////////////////////////////////////// @@ -143,19 +157,19 @@ public class SellerAsTakerProtocol { taskRunner.run(); } - private void handleBankTransferStartedMessage(BankTransferStartedMessage tradeMessage) { + private void handleFiatTransferStartedMessage(FiatTransferStartedMessage tradeMessage) { model.setTradeMessage(tradeMessage); TaskRunner taskRunner = new TaskRunner<>(model, () -> { - log.debug("taskRunner at handleBankTransferInitedMessage completed"); + log.debug("taskRunner at handleFiatTransferStartedMessage completed"); model.trade.setState(Trade.State.FIAT_PAYMENT_STARTED); }, (errorMessage) -> { log.error(errorMessage); } ); - taskRunner.addTasks(ProcessBankTransferStartedMessage.class); + taskRunner.addTasks(ProcessFiatTransferStartedMessage.class); taskRunner.run(); } @@ -201,8 +215,8 @@ public class SellerAsTakerProtocol { else if (tradeMessage instanceof DepositTxPublishedMessage) { handleDepositTxPublishedMessage((DepositTxPublishedMessage) tradeMessage); } - else if (tradeMessage instanceof BankTransferStartedMessage) { - handleBankTransferStartedMessage((BankTransferStartedMessage) tradeMessage); + else if (tradeMessage instanceof FiatTransferStartedMessage) { + handleFiatTransferStartedMessage((FiatTransferStartedMessage) tradeMessage); } else { log.error("Incoming message not supported. " + tradeMessage); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/models/SellerAsTakerModel.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/models/SellerAsTakerModel.java index 82eb18d237..a7b56b5514 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/models/SellerAsTakerModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/models/SellerAsTakerModel.java @@ -20,6 +20,7 @@ package io.bitsquare.trade.protocol.trade.taker.models; import io.bitsquare.btc.BlockChainService; import io.bitsquare.btc.WalletService; import io.bitsquare.crypto.SignatureService; +import io.bitsquare.p2p.MailboxService; import io.bitsquare.p2p.MessageService; import io.bitsquare.p2p.Peer; import io.bitsquare.persistence.Persistence; @@ -49,6 +50,7 @@ public class SellerAsTakerModel extends SharedTradeModel implements Serializable public SellerAsTakerModel(Trade trade, Peer offererPeer, MessageService messageService, + MailboxService mailboxService, WalletService walletService, BlockChainService blockChainService, SignatureService signatureService, @@ -56,6 +58,7 @@ public class SellerAsTakerModel extends SharedTradeModel implements Serializable Persistence persistence) { super(trade.getOffer(), messageService, + mailboxService, walletService, blockChainService, signatureService, @@ -87,6 +90,7 @@ public class SellerAsTakerModel extends SharedTradeModel implements Serializable taker.fiatAccount = user.getBankAccount(offer.getBankAccountId()); taker.accountId = user.getAccountId(); taker.p2pSigPubKey = user.getP2PSigPubKey(); + taker.p2pEncryptPublicKey = user.getP2PEncryptPubKey(); taker.pubKey = taker.addressEntry.getPubKey(); } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/models/TakerModel.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/models/TakerModel.java index 20f4e0dcea..b62becfc69 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/models/TakerModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/models/TakerModel.java @@ -39,6 +39,7 @@ public class TakerModel implements Serializable { transient public FiatAccount fiatAccount; transient public String accountId; transient public PublicKey p2pSigPubKey; + transient public PublicKey p2pEncryptPublicKey; transient public byte[] registrationPubKey; // TODO not read yet, missing impl. transient public DeterministicKey registrationKeyPair; transient public AddressEntry addressEntry; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/CreateAndSignContract.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/CreateAndSignContract.java index 2674f7046d..df60eb618a 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/CreateAndSignContract.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/CreateAndSignContract.java @@ -45,7 +45,7 @@ public class CreateAndSignContract extends Task { model.taker.accountId, model.offerer.fiatAccount, model.taker.fiatAccount, - model.offer.getMessagePublicKey(), + model.offer.getP2PSigPubKey(), model.taker.p2pSigPubKey); String contractAsJson = Utilities.objectToJson(contract); String signature = model.signatureService.signMessage(model.taker.registrationKeyPair, contractAsJson); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessBankTransferStartedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessFiatTransferStartedMessage.java similarity index 86% rename from core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessBankTransferStartedMessage.java rename to core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessFiatTransferStartedMessage.java index 27a690bffe..71aba83dcf 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessBankTransferStartedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessFiatTransferStartedMessage.java @@ -19,7 +19,7 @@ package io.bitsquare.trade.protocol.trade.taker.tasks; import io.bitsquare.common.taskrunner.Task; import io.bitsquare.common.taskrunner.TaskRunner; -import io.bitsquare.trade.protocol.trade.messages.BankTransferStartedMessage; +import io.bitsquare.trade.protocol.trade.messages.FiatTransferStartedMessage; import io.bitsquare.trade.protocol.trade.taker.models.SellerAsTakerModel; import org.slf4j.Logger; @@ -28,10 +28,10 @@ import org.slf4j.LoggerFactory; import static com.google.common.base.Preconditions.checkNotNull; import static io.bitsquare.util.Validator.*; -public class ProcessBankTransferStartedMessage extends Task { - private static final Logger log = LoggerFactory.getLogger(ProcessBankTransferStartedMessage.class); +public class ProcessFiatTransferStartedMessage extends Task { + private static final Logger log = LoggerFactory.getLogger(ProcessFiatTransferStartedMessage.class); - public ProcessBankTransferStartedMessage(TaskRunner taskHandler, SellerAsTakerModel model) { + public ProcessFiatTransferStartedMessage(TaskRunner taskHandler, SellerAsTakerModel model) { super(taskHandler, model); } @@ -39,7 +39,7 @@ public class ProcessBankTransferStartedMessage extends Task protected void doRun() { try { checkTradeId(model.id, model.getTradeMessage()); - BankTransferStartedMessage message = (BankTransferStartedMessage) model.getTradeMessage(); + FiatTransferStartedMessage message = (FiatTransferStartedMessage) model.getTradeMessage(); model.offerer.signature = checkNotNull(message.offererSignature); model.offerer.payoutAmount = positiveCoinOf(nonZeroCoinOf(message.offererPayoutAmount)); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SendSignedTakerDepositTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SendSignedTakerDepositTx.java index c481fceec3..087d3457c5 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SendSignedTakerDepositTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SendSignedTakerDepositTx.java @@ -40,6 +40,7 @@ public class SendSignedTakerDepositTx extends Task { model.taker.fiatAccount, model.taker.accountId, model.taker.p2pSigPubKey, + model.taker.p2pEncryptPublicKey, model.trade.getContractAsJson(), model.trade.getTakerContractSignature(), model.taker.addressEntry.getAddressString(), diff --git a/core/src/main/java/io/bitsquare/user/User.java b/core/src/main/java/io/bitsquare/user/User.java index ebbd0e97f2..40f683a6fa 100644 --- a/core/src/main/java/io/bitsquare/user/User.java +++ b/core/src/main/java/io/bitsquare/user/User.java @@ -24,6 +24,8 @@ import io.bitsquare.util.DSAKeyUtil; import java.io.Serializable; import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; import java.security.PublicKey; import java.util.ArrayList; @@ -39,12 +41,16 @@ import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * The User is persisted locally. * It must never be transmitted over the wire (messageKeyPair contains private key!). */ public class User implements Serializable { private static final long serialVersionUID = 7409078808248518638L; + private static final Logger log = LoggerFactory.getLogger(User.class); private KeyPair p2pSigKeyPair; private KeyPair p2pEncryptKeyPair; @@ -87,8 +93,13 @@ public class User implements Serializable { } else { // First time - p2pSigKeyPair = DSAKeyUtil.generateKeyPair(); - p2pEncryptKeyPair = encryptionService.getKeyPair(); + p2pSigKeyPair = DSAKeyUtil.generateDSAKeyPair(); + try { + p2pEncryptKeyPair = encryptionService.getKeyPair(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + log.error(e.getMessage()); + } } } @@ -179,6 +190,10 @@ public class User implements Serializable { return p2pSigKeyPair.getPublic(); } + public PublicKey getP2PEncryptPubKey() { + return p2pEncryptKeyPair.getPublic(); + } + public ObjectProperty currentBankAccountProperty() { return currentBankAccount; } @@ -192,7 +207,11 @@ public class User implements Serializable { return _currentFiatAccount; } - public KeyPair getP2pEncryptKeyPair() { + public PrivateKey getP2pEncryptPrivateKey() { + return p2pEncryptKeyPair.getPrivate(); + } + + KeyPair getP2pEncryptKeyPair() { return p2pEncryptKeyPair; } } diff --git a/core/src/main/java/io/bitsquare/util/DSAKeyUtil.java b/core/src/main/java/io/bitsquare/util/DSAKeyUtil.java index a3ff525918..70d9464216 100755 --- a/core/src/main/java/io/bitsquare/util/DSAKeyUtil.java +++ b/core/src/main/java/io/bitsquare/util/DSAKeyUtil.java @@ -36,7 +36,7 @@ public class DSAKeyUtil { return Utils.HEX.encode(x509EncodedKeySpec.getEncoded()); } - public static KeyPair generateKeyPair() { + public static KeyPair generateDSAKeyPair() { try { final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA"); keyGen.initialize(1024); diff --git a/core/src/test/java/io/bitsquare/crypto/EncryptionServiceTests.java b/core/src/test/java/io/bitsquare/crypto/EncryptionServiceTests.java new file mode 100644 index 0000000000..f6c20b6c71 --- /dev/null +++ b/core/src/test/java/io/bitsquare/crypto/EncryptionServiceTests.java @@ -0,0 +1,88 @@ +/* + * 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 . + */ + +package io.bitsquare.crypto; + +import io.bitsquare.p2p.MailboxMessage; + +import java.security.KeyPair; + +import java.util.Random; + +import org.junit.Test; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.springframework.test.util.AssertionErrors.assertEquals; + +public class EncryptionServiceTests { + private static final Logger log = LoggerFactory.getLogger(EncryptionServiceTests.class); + + @Test + public void testEncryptionWithMailboxMessage() throws Exception { + EncryptionService encryptionService = new EncryptionService<>(); + KeyPair p2pEncryptKeyPair = encryptionService.getKeyPair(); + + TestMessage message = new TestMessage("test"); + EncryptionPackage encryptionPackage = encryptionService.encryptObject(p2pEncryptKeyPair.getPublic(), message); + MailboxMessage result = encryptionService.decryptToObject(p2pEncryptKeyPair.getPrivate(), encryptionPackage); + assertEquals("", message.data, ((TestMessage) result).data); + } + + @Test + public void testEncryptionWithInteger() throws Exception { + EncryptionService encryptionService = new EncryptionService<>(); + KeyPair p2pEncryptKeyPair = encryptionService.getKeyPair(); + int data = 1234; + EncryptionPackage encryptionPackage = encryptionService.encryptObject(p2pEncryptKeyPair.getPublic(), data); + Integer result = encryptionService.decryptToObject(p2pEncryptKeyPair.getPrivate(), encryptionPackage); + assertEquals("", data, result); + } + + @Test + public void testEncryptionWithBytes() throws Exception { + EncryptionService encryptionService = new EncryptionService(); + KeyPair p2pEncryptKeyPair = encryptionService.getKeyPair(); + + byte[] data = new byte[]{0x00, 0x01, 0x02, 0x03, 0x04}; + EncryptionPackage encryptionPackage = encryptionService.encrypt(p2pEncryptKeyPair.getPublic(), data); + byte[] result = encryptionService.decrypt(p2pEncryptKeyPair.getPrivate(), encryptionPackage); + assertEquals("", result, data); + } + + @Test + public void testEncryptionWithLargeData() throws Exception { + EncryptionService encryptionService = new EncryptionService(); + KeyPair p2pEncryptKeyPair = encryptionService.getKeyPair(); + + byte[] data = new byte[2000]; + new Random().nextBytes(data); + + EncryptionPackage encryptionPackage = encryptionService.encrypt(p2pEncryptKeyPair.getPublic(), data); + byte[] result = encryptionService.decrypt(p2pEncryptKeyPair.getPrivate(), encryptionPackage); + assertEquals("", result, data); + } +} + +class TestMessage implements MailboxMessage { + public String data = "test"; + + public TestMessage(String data) { + this.data = data; + } +} diff --git a/core/src/test/java/io/bitsquare/trade/protocol/placeoffer/PlaceOfferProtocolTest.java b/core/src/test/java/io/bitsquare/trade/protocol/placeoffer/PlaceOfferProtocolTest.java index 88522615cd..3efd4654bb 100644 --- a/core/src/test/java/io/bitsquare/trade/protocol/placeoffer/PlaceOfferProtocolTest.java +++ b/core/src/test/java/io/bitsquare/trade/protocol/placeoffer/PlaceOfferProtocolTest.java @@ -296,7 +296,7 @@ public class PlaceOfferProtocolTest { private Offer getOffer() { return new Offer(OFFER_ID, - DSAKeyUtil.generateKeyPair().getPublic(), + DSAKeyUtil.generateDSAKeyPair().getPublic(), Direction.BUY, 100L, Coin.CENT,