AddressEntryList save/load cycle works

This commit is contained in:
Mike Rosseel 2017-03-31 16:44:36 +02:00
parent f1ef154b61
commit ce6620a291
88 changed files with 348 additions and 281 deletions

View file

@ -1,4 +1,4 @@
package io.bisq.network.p2p;
package io.bisq.common.persistance;
import io.bisq.common.Marshaller;

View file

@ -15,13 +15,13 @@
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.network.p2p.network;
package io.bisq.common.persistance;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import java.util.Optional;
public interface ProtobufferResolver {
Optional<Msg> fromProto(PB.Envelope envelope);
Optional<Persistable> fromProto(PB.DiskEnvelope envelope);
}

View file

@ -17,16 +17,20 @@
package io.bisq.common.storage;
import com.google.inject.Inject;
import com.google.protobuf.Message;
import io.bisq.common.UserThread;
import io.bisq.common.io.LookAheadObjectInputStream;
import io.bisq.common.persistance.Persistable;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.util.Utilities;
import io.bisq.generated.protobuffer.PB;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@ -43,17 +47,17 @@ public class FileManager<T> {
private final long delay;
private final Callable<Void> saveFileTask;
private T serializable;
private boolean proto;
private ProtobufferResolver protobufferResolver;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public FileManager(File dir, File storageFile, long delay) {
@Inject
public FileManager(File dir, File storageFile, long delay, ProtobufferResolver protobufferResolver) {
this.dir = dir;
this.storageFile = storageFile;
proto = serializable instanceof Persistable;
this.protobufferResolver = protobufferResolver;
executor = Utilities.getScheduledThreadPoolExecutor("FileManager", 1, 10, 5);
@ -106,13 +110,17 @@ public class FileManager<T> {
public synchronized T read(File file) throws IOException, ClassNotFoundException {
log.debug("read" + file);
if (proto) {
log.info("it's proto");
try (final FileInputStream fileInputStream = new FileInputStream(file)) {
return (T) ((Persistable) serializable).getParser().parseFrom(fileInputStream);
} catch (Throwable t) {
log.error("Exception at proto read: " + t.getMessage());
}
Optional<Persistable> persistable = Optional.empty();
try (final FileInputStream fileInputStream = new FileInputStream(file)) {
persistable = protobufferResolver.fromProto(PB.DiskEnvelope.parseFrom(fileInputStream));
} catch (Throwable t) {
log.error("Exception at proto read: " + t.getMessage() + " " + file.getName());
}
if(persistable.isPresent()) {
log.error("Persistable found");
return (T) persistable.get();
}
try (final FileInputStream fileInputStream = new FileInputStream(file);
@ -192,7 +200,7 @@ public class FileManager<T> {
try {
message = ((Persistable) serializable).toProtobuf();
} catch (Throwable e) {
log.info("Not protobufferable: {} {}", serializable.getClass().getSimpleName(), e.getMessage());
log.info("Not protobufferable: {}, {}, {}", serializable.getClass().getSimpleName(), storageFile, e.getMessage());
}
try {

View file

@ -17,11 +17,12 @@
package io.bisq.common.storage;
import com.google.inject.Inject;
import io.bisq.common.persistance.ProtobufferResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.File;
import java.io.IOException;
@ -66,6 +67,8 @@ public class Storage<T extends Serializable> {
private T serializable;
private String fileName;
private int numMaxBackupFiles = 10;
@com.google.inject.Inject
private ProtobufferResolver protobufferResolver;
///////////////////////////////////////////////////////////////////////////////////////////
@ -73,21 +76,28 @@ public class Storage<T extends Serializable> {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public Storage(@Named(DIR_KEY) File dir) {
public Storage(@Named(DIR_KEY) File dir, ProtobufferResolver protobufferResolver) {
this.dir = dir;
this.protobufferResolver = protobufferResolver;
}
/*
public Storage(@Named(DIR_KEY) File dir) {
this.dir = dir;
this.protobufferResolver = protobufferResolver;
}
*/
public void initWithFileName(String fileName) {
this.fileName = fileName;
storageFile = new File(dir, fileName);
fileManager = new FileManager<>(dir, storageFile, 300);
fileManager = new FileManager<>(dir, storageFile, 300, protobufferResolver);
}
@Nullable
public T initAndGetPersistedWithFileName(String fileName) {
this.fileName = fileName;
storageFile = new File(dir, fileName);
fileManager = new FileManager<>(dir, storageFile, 300);
fileManager = new FileManager<>(dir, storageFile, 300, protobufferResolver);
return getPersisted();
}
@ -102,7 +112,7 @@ public class Storage<T extends Serializable> {
this.serializable = serializable;
this.fileName = fileName;
storageFile = new File(dir, fileName);
fileManager = new FileManager<>(dir, storageFile, 600);
fileManager = new FileManager<>(dir, storageFile, 600, protobufferResolver);
return getPersisted();
}
@ -151,7 +161,7 @@ public class Storage<T extends Serializable> {
// Private
///////////////////////////////////////////////////////////////////////////////////////////
// We do the file read on the UI thread to avoid problems from multi threading.
// We do the file read on the UI thread to avoid problems from multi threading.
// Data are small and read is done only at startup, so it is no performance issue.
@Nullable
private T getPersisted() {
@ -161,7 +171,7 @@ public class Storage<T extends Serializable> {
T persistedObject = fileManager.read(storageFile);
log.trace("Read {} completed in {}msec", storageFile, System.currentTimeMillis() - now);
// If we did not get any exception we can be sure the data are consistent so we make a backup
// If we did not get any exception we can be sure the data are consistent so we make a backup
now = System.currentTimeMillis();
fileManager.backupFile(fileName, numMaxBackupFiles);
log.trace("Backup {} completed in {}msec", storageFile, System.currentTimeMillis() - now);

View file

@ -776,12 +776,16 @@ message AddressEntry {
string offer_id = 7;
Context context = 8;
bytes pubkey = 9;
bytes pub_key = 9;
bytes pub_key_hash = 10;
string param_id = 11;
Coin coin_locked_in_multi_sig = 12;
}
message DeterministicKey {
bytes serialized = 1;
}
message AddressEntryList {
repeated AddressEntry address_entry = 1;
}

View file

@ -10,8 +10,8 @@
<appender-ref ref="CONSOLE_APPENDER"/>
</root>
<logger name="io.bisq.common.storage.Storage" level="WARN"/>
<logger name="io.bisq.common.storage.FileManager" level="WARN"/>
<logger name="io.bisq.core.storage.Storage" level="WARN"/>
<logger name="io.bisq.core.storage.FileManager" level="WARN"/>
<logger name="io.bisq.locale.BSResources" level="ERROR"/>
<!-- <logger name="io.bisq.p2p.peers.PeerGroup" level="TRACE"/>

View file

@ -22,6 +22,7 @@ import com.google.inject.name.Named;
import io.bisq.common.app.DevEnv;
import io.bisq.common.crypto.KeyRing;
import io.bisq.common.crypto.PubKeyRing;
import io.bisq.common.persistance.Msg;
import io.bisq.core.app.AppOptionKeys;
import io.bisq.network.p2p.*;
import javafx.beans.property.ObjectProperty;

View file

@ -3,7 +3,7 @@ package io.bisq.core.alert;
import io.bisq.common.app.Version;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.MailboxMsg;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode;
import lombok.ToString;

View file

@ -27,6 +27,8 @@ import io.bisq.common.crypto.PubKeyRing;
import io.bisq.common.handlers.FaultHandler;
import io.bisq.common.handlers.ResultHandler;
import io.bisq.common.locale.Res;
import io.bisq.common.persistance.Msg;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.storage.Storage;
import io.bisq.core.arbitration.messages.*;
import io.bisq.core.btc.AddressEntry;
@ -89,6 +91,7 @@ public class DisputeManager {
ClosedTradableManager closedTradableManager,
OpenOfferManager openOfferManager,
KeyRing keyRing,
ProtobufferResolver protobufferResolver,
@Named(Storage.DIR_KEY) File storageDir) {
this.p2PService = p2PService;
this.tradeWalletService = tradeWalletService;
@ -98,7 +101,7 @@ public class DisputeManager {
this.openOfferManager = openOfferManager;
this.keyRing = keyRing;
disputeStorage = new Storage<>(storageDir);
disputeStorage = new Storage<>(storageDir, protobufferResolver);
disputes = new DisputeList(disputeStorage);
openDisputes = new HashMap<>();
@ -148,7 +151,7 @@ public class DisputeManager {
openDisputes.put(dispute.getTradeId(), dispute);
});
// If we have duplicate disputes we close the second one (might happen if both traders opened a dispute and arbitrator
// If we have duplicate disputes we close the second one (might happen if both traders opened a dispute and arbitrator
// was offline, so could not forward msg to other peer, then the arbitrator might have 4 disputes open for 1 trade)
openDisputes.entrySet().stream().forEach(openDisputeEntry -> {
String key = openDisputeEntry.getKey();
@ -354,7 +357,7 @@ public class DisputeManager {
false,
UUID.randomUUID().toString()
);
disputeCommunicationMessage.addAllAttachments(attachments);
PubKeyRing receiverPubKeyRing = null;
NodeAddress peerNodeAddress = null;
@ -415,7 +418,7 @@ public class DisputeManager {
false,
UUID.randomUUID().toString()
);
dispute.addDisputeMessage(disputeCommunicationMessage);
disputeResult.setDisputeCommunicationMessage(disputeCommunicationMessage);
@ -583,7 +586,7 @@ public class DisputeManager {
// We need to avoid publishing the tx from both traders as it would create problems with zero confirmation withdrawals
// There would be different transactions if both sign and publish (signers: once buyer+arb, once seller+arb)
// The tx publisher is the winner or in case both get 50% the buyer, as the buyer has more inventive to publish the tx as he receives
// The tx publisher is the winner or in case both get 50% the buyer, as the buyer has more inventive to publish the tx as he receives
// more BTC as he has deposited
final Contract contract = dispute.getContract();
@ -645,7 +648,7 @@ public class DisputeManager {
dispute.setDisputePayoutTxId(transaction.getHashAsString());
sendPeerPublishedPayoutTxMessage(transaction, dispute, contract);
// set state after payout as we call swapTradeEntryToAvailableEntry
// set state after payout as we call swapTradeEntryToAvailableEntry
if (tradeManager.getTradeById(dispute.getTradeId()).isPresent())
tradeManager.closeDisputedTrade(dispute.getTradeId());
else {

View file

@ -20,7 +20,7 @@ package io.bisq.core.arbitration.messages;
import io.bisq.common.app.Version;
import io.bisq.core.arbitration.Attachment;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
@ -38,7 +38,7 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@EqualsAndHashCode
@EqualsAndHashCode(callSuper = true)
@ToString
@Getter
@Slf4j

View file

@ -20,7 +20,7 @@ package io.bisq.core.arbitration.messages;
import io.bisq.common.app.Version;
import io.bisq.core.arbitration.DisputeResult;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode;
import lombok.ToString;

View file

@ -20,7 +20,7 @@ package io.bisq.core.arbitration.messages;
import io.bisq.common.app.Version;
import io.bisq.core.arbitration.Dispute;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode;
import lombok.ToString;

View file

@ -20,7 +20,7 @@ package io.bisq.core.arbitration.messages;
import io.bisq.common.app.Version;
import io.bisq.core.arbitration.Dispute;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode;
import lombok.ToString;

View file

@ -20,7 +20,7 @@ package io.bisq.core.arbitration.messages;
import com.google.protobuf.ByteString;
import io.bisq.common.app.Version;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode;

View file

@ -110,12 +110,14 @@ public final class AddressEntry implements Persistable {
String paramId,
Context context,
@Nullable String offerId,
@Nullable Coin coinLockedInMultiSig,
@NotNull KeyBagSupplier keyBagSupplier) {
this.pubKey = pubKey;
this.pubKeyHash = pubKeyHash;
this.paramId = paramId;
this.context = context;
this.offerId = offerId;
this.coinLockedInMultiSig = coinLockedInMultiSig;
this.keyBagSupplier = keyBagSupplier;
}
@ -210,7 +212,7 @@ public final class AddressEntry implements Persistable {
public Message toProtobuf() {
PB.AddressEntry.Builder builder = PB.AddressEntry.newBuilder()
.setContext(PB.AddressEntry.Context.valueOf(context.name()))
.setPubkey(ByteString.copyFrom(pubKey))
.setPubKey(ByteString.copyFrom(pubKey))
.setPubKeyHash(ByteString.copyFrom(pubKeyHash))
.setParamId(paramId);
Optional.ofNullable(offerId).ifPresent(builder::setOfferId);

View file

@ -22,6 +22,7 @@ import com.google.protobuf.Message;
import io.bisq.common.app.Version;
import io.bisq.common.persistance.Persistable;
import io.bisq.common.storage.Storage;
import io.bisq.core.btc.wallet.KeyBagSupplier;
import io.bisq.generated.protobuffer.PB;
import lombok.Getter;
import org.bitcoinj.core.Wallet;
@ -43,13 +44,16 @@ public final class AddressEntryList implements Persistable {
private static final Logger log = LoggerFactory.getLogger(AddressEntryList.class);
final transient private Storage<AddressEntryList> storage;
@Getter
final transient private KeyBagSupplier keyBagSupplier;
transient private Wallet wallet;
@Getter
private List<AddressEntry> addressEntryList = new ArrayList<>();
@Inject
public AddressEntryList(Storage<AddressEntryList> storage) {
public AddressEntryList(Storage<AddressEntryList> storage, KeyBagSupplier keyBagSupplier) {
this.storage = storage;
this.keyBagSupplier = keyBagSupplier;
}
public void onWalletReady(Wallet wallet) {

View file

@ -22,10 +22,7 @@ import com.google.inject.name.Names;
import io.bisq.common.app.AppModule;
import io.bisq.core.app.AppOptionKeys;
import io.bisq.core.btc.provider.squ.BsqUtxoFeedService;
import io.bisq.core.btc.wallet.BsqWalletService;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.btc.wallet.TradeWalletService;
import io.bisq.core.btc.wallet.WalletsSetup;
import io.bisq.core.btc.wallet.*;
import io.bisq.core.provider.fee.FeeService;
import io.bisq.core.provider.price.PriceFeedService;
import io.bisq.network.http.HttpClient;
@ -64,6 +61,7 @@ public class BitcoinModule extends AppModule {
bind(AddressEntryList.class).in(Singleton.class);
bind(WalletsSetup.class).in(Singleton.class);
bind(BtcWalletService.class).in(Singleton.class);
bind(KeyBagSupplier.class).to(BtcWalletService.class);
bind(BsqWalletService.class).in(Singleton.class);
bind(TradeWalletService.class).in(Singleton.class);

View file

@ -175,7 +175,7 @@ public class BtcWalletService extends WalletService implements KeyBagSupplier {
// mining fee: BTC mining fee + optional burned BSQ fee (only if opReturnData != null)
// In case of txs for burned BSQ fees we have no receiver output and it might be that there is no change outputs
// We need to guarantee that min. 1 valid output is added (OP_RETURN does not count). So we use a higher input
// We need to guarantee that min. 1 valid output is added (OP_RETURN does not count). So we use a higher input
// for BTC to force an additional change output.
// safety check counter to avoid endless loops
@ -232,7 +232,7 @@ public class BtcWalletService extends WalletService implements KeyBagSupplier {
resultTx = sendRequest.tx;
// We might have the rare case that both inputs matched the required fees, so both did not require
// We might have the rare case that both inputs matched the required fees, so both did not require
// a change output.
// In such cases we need to add artificially a change output (OP_RETURN is not allowed as only output)
forcedChangeValue = resultTx.getOutputs().size() == 0 ? Transaction.MIN_NONDUST_OUTPUT : Coin.ZERO;
@ -266,7 +266,7 @@ public class BtcWalletService extends WalletService implements KeyBagSupplier {
}
///////////////////////////////////////////////////////////////////////////////////////////
// Commit tx
// Commit tx
///////////////////////////////////////////////////////////////////////////////////////////
public void commitTx(Transaction tx) {

View file

@ -17,7 +17,6 @@
package io.bisq.core.btc.wallet;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
@ -48,15 +47,13 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.inject.internal.util.$Preconditions.checkArgument;
import static com.google.inject.internal.util.$Preconditions.checkState;
import static com.google.common.base.Preconditions.*;
// TradeService handles all relevant transactions used in the trade process
/*
To maintain a consistent tx structure we use that structure:
To maintain a consistent tx structure we use that structure:
Always buyers in/outputs/keys first then sellers in/outputs/keys the arbitrators outputs/keys.
Deposit tx:
IN[0] buyer (mandatory) e.g. 0.1 BTC
IN[...] optional additional buyer inputs (normally never used as we pay from trade fee tx and always have 1 output there)
@ -67,30 +64,30 @@ import static com.google.inject.internal.util.$Preconditions.checkState;
OUT[...] optional buyer change (normally never used as we pay from trade fee tx and always have 1 output there)
OUT[...] optional seller change (normally never used as we pay from trade fee tx and always have 1 output there)
FEE tx fee 0.0001 BTC
Payout tx:
IN[0] Multisig output from deposit Tx (signed by buyer and trader)
OUT[0] Buyer payout address
OUT[1] Seller payout address
We use 0 confirmation transactions to make the trade process practical from usability side.
We use 0 confirmation transactions to make the trade process practical from usability side.
There is no risk for double spends as the deposit transaction would become invalid if any preceding transaction would have been double spent.
If a preceding transaction in the chain will not make it into the same or earlier block as the deposit transaction the deposit transaction
will be invalid as well.
If a preceding transaction in the chain will not make it into the same or earlier block as the deposit transaction the deposit transaction
will be invalid as well.
Though the deposit need 1 confirmation before the buyer starts the Fiat payment.
We have that chain of transactions:
1. Deposit from external wallet to our trading wallet: Tx0 (0 conf)
2. Create offer (or take offer) fee payment from Tx0 output: tx1 (0 conf)
3. Deposit tx created with inputs from tx1 of both traders: Tx2 (here we wait for 1 conf)
Fiat transaction will not start before we get at least 1 confirmation for the deposit tx, then we can proceed.
4. Payout tx with input from MS output and output to both traders: Tx3 (0 conf)
5. Withdrawal to external wallet from Tx3: Tx4 (0 conf)
After the payout transaction we also don't have issues with 0 conf or if not both tx (payout, withdrawal) make it into a block.
After the payout transaction we also don't have issues with 0 conf or if not both tx (payout, withdrawal) make it into a block.
Worst case is to rebroadcast the transactions (TODO: is not implemented yet).
*/
public class TradeWalletService {
private static final Logger log = LoggerFactory.getLogger(TradeWalletService.class);
@ -191,7 +188,7 @@ public class TradeWalletService {
///////////////////////////////////////////////////////////////////////////////////////////
// We construct the deposit transaction in the way that the buyer is always the first entry (inputs, outputs, MS keys) and then the seller.
// In the creation of the deposit tx the taker/maker roles are the determining roles instead of buyer/seller.
// In the creation of the deposit tx the taker/maker roles are the determining roles instead of buyer/seller.
// In the payout tx is is the buyer/seller role. We keep the buyer/seller ordering over all transactions to not get confusion with ordering,
// which is important to follow correctly specially for the order of the MS keys.
@ -221,12 +218,12 @@ public class TradeWalletService {
/*
The tx we create has that structure:
IN[0] any input > inputAmount (including tx fee) (unsigned)
IN[1...n] optional inputs supported, but normally there is just 1 input (unsigned)
OUT[0] dummyOutputAmount (inputAmount - tx fee)
OUT[1] Optional Change = inputAmount - dummyOutputAmount - tx fee
We are only interested in the inputs and the optional change output.
*/
@ -241,7 +238,7 @@ public class TradeWalletService {
dummyTX.addOutput(dummyOutput);
// Find the needed inputs to pay the output, optionally add 1 change output.
// Normally only 1 input and no change output is used, but we support multiple inputs and 1 change output.
// Normally only 1 input and no change output is used, but we support multiple inputs and 1 change output.
// Our spending transaction output is from the create offer fee payment.
addAvailableInputsAndChangeOutputs(dummyTX, takersAddress, takersChangeAddress, txFee);
@ -325,7 +322,7 @@ public class TradeWalletService {
checkArgument(!takerRawTransactionInputs.isEmpty());
// First we construct a dummy TX to get the inputs and outputs we want to use for the real deposit tx.
// First we construct a dummy TX to get the inputs and outputs we want to use for the real deposit tx.
// Similar to the way we did in the createTakerDepositTxInputs method.
Transaction dummyTx = new Transaction(params);
TransactionOutput dummyOutput = new TransactionOutput(params, dummyTx, makerInputAmount, new ECKey().toAddress(params));
@ -336,7 +333,7 @@ public class TradeWalletService {
TransactionOutput makerOutput = null;
// We don't support more then 1 optional change output
Preconditions.checkArgument(dummyTx.getOutputs().size() < 3, "dummyTx.getOutputs().size() >= 3");
checkArgument(dummyTx.getOutputs().size() < 3, "dummyTx.getOutputs().size() >= 3");
// Only save change outputs, the dummy output is ignored (that's why we start with index 1)
if (dummyTx.getOutputs().size() > 1)
@ -391,26 +388,26 @@ public class TradeWalletService {
new Address(params, takerChangeAddressString));
if (makerIsBuyer) {
// Add optional buyer outputs
// Add optional buyer outputs
if (makerOutput != null)
preparedDepositTx.addOutput(makerOutput);
// Add optional seller outputs
// Add optional seller outputs
if (takerTransactionOutput != null)
preparedDepositTx.addOutput(takerTransactionOutput);
} else {
// taker is buyer role
// Add optional seller outputs
// Add optional seller outputs
if (takerTransactionOutput != null)
preparedDepositTx.addOutput(takerTransactionOutput);
// Add optional buyer outputs
// Add optional buyer outputs
if (makerOutput != null)
preparedDepositTx.addOutput(makerOutput);
}
// Sign inputs
// Sign inputs
int start = makerIsBuyer ? 0 : takerRawTransactionInputs.size();
int end = makerIsBuyer ? makerInputs.size() : preparedDepositTx.getInputs().size();
for (int i = start; i < end; i++) {
@ -471,7 +468,7 @@ public class TradeWalletService {
if (!makersDepositTx.getOutput(0).getScriptPubKey().equals(p2SHMultiSigOutputScript))
throw new TransactionVerificationException("Maker's p2SHMultiSigOutputScript does not match to takers p2SHMultiSigOutputScript");
// The outpoints are not available from the serialized makersDepositTx, so we cannot use that tx directly, but we use it to construct a new
// The outpoints are not available from the serialized makersDepositTx, so we cannot use that tx directly, but we use it to construct a new
// depositTx
Transaction depositTx = new Transaction(params);
@ -481,7 +478,7 @@ public class TradeWalletService {
for (int i = 0; i < buyerInputs.size(); i++)
depositTx.addInput(getTransactionInput(depositTx, getScriptProgram(makersDepositTx, i), buyerInputs.get(i)));
// Add seller inputs
// Add seller inputs
for (RawTransactionInput rawTransactionInput : sellerInputs)
depositTx.addInput(getTransactionInput(depositTx, new byte[]{}, rawTransactionInput));
} else {
@ -490,7 +487,7 @@ public class TradeWalletService {
for (RawTransactionInput rawTransactionInput : buyerInputs)
depositTx.addInput(getTransactionInput(depositTx, new byte[]{}, rawTransactionInput));
// Add seller inputs
// Add seller inputs
// We grab the signature from the makersDepositTx and apply it to the new tx input
for (int i = buyerInputs.size(), k = 0; i < makersDepositTx.getInputs().size(); i++, k++)
depositTx.addInput(getTransactionInput(depositTx, getScriptProgram(makersDepositTx, i), sellerInputs.get(k)));
@ -509,7 +506,7 @@ public class TradeWalletService {
makersDepositTx.getOutputs().forEach(depositTx::addOutput);
//BtcWalletService.printTx("makersDepositTx", makersDepositTx);
// Sign inputs
// Sign inputs
int start = takerIsSeller ? buyerInputs.size() : 0;
int end = takerIsSeller ? depositTx.getInputs().size() : buyerInputs.size();
for (int i = start; i < end; i++) {
@ -938,7 +935,7 @@ public class TradeWalletService {
public Transaction addTxToWallet(Transaction transaction) throws VerificationException {
Log.traceCall("transaction " + transaction.toString());
// We need to recreate the transaction otherwise we get a null pointer...
// We need to recreate the transaction otherwise we get a null pointer...
Transaction result = new Transaction(params, transaction.bitcoinSerialize());
result.getConfidence(Context.get()).setSource(TransactionConfidence.Source.SELF);
@ -955,7 +952,7 @@ public class TradeWalletService {
public Transaction addTxToWallet(byte[] serializedTransaction) throws VerificationException {
Log.traceCall();
// We need to recreate the tx otherwise we get a null pointer...
// We need to recreate the tx otherwise we get a null pointer...
Transaction transaction = new Transaction(params, serializedTransaction);
transaction.getConfidence(Context.get()).setSource(TransactionConfidence.Source.NETWORK);
log.trace("transaction from serializedTransaction: " + transaction.toString());
@ -1007,11 +1004,11 @@ public class TradeWalletService {
}
// Don't use ScriptBuilder.createRedeemScript and ScriptBuilder.createP2SHOutputScript as they use a sorting
// (Collections.sort(pubKeys, ECKey.PUBKEY_COMPARATOR);) which can lead to a non-matching list of signatures with pubKeys and the executeMultiSig does
// not iterate all possible combinations of sig/pubKeys leading to a verification fault. That nasty bug happens just randomly as the list after sorting
// Don't use ScriptBuilder.createRedeemScript and ScriptBuilder.createP2SHOutputScript as they use a sorting
// (Collections.sort(pubKeys, ECKey.PUBKEY_COMPARATOR);) which can lead to a non-matching list of signatures with pubKeys and the executeMultiSig does
// not iterate all possible combinations of sig/pubKeys leading to a verification fault. That nasty bug happens just randomly as the list after sorting
// might differ from the provided one or not.
// Changing the while loop in executeMultiSig to fix that does not help as the reference implementation seems to behave the same (not iterating all
// Changing the while loop in executeMultiSig to fix that does not help as the reference implementation seems to behave the same (not iterating all
// possibilities) .
// Furthermore the executed list is reversed to the provided.
// Best practice is to provide the list sorted by the least probable successful candidates first (arbitrator is first -> will be last in execution loop, so

View file

@ -27,6 +27,7 @@ import io.bisq.common.UserThread;
import io.bisq.common.app.Log;
import io.bisq.common.handlers.ExceptionHandler;
import io.bisq.common.handlers.ResultHandler;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.storage.FileUtil;
import io.bisq.common.storage.Storage;
import io.bisq.core.btc.*;
@ -59,9 +60,9 @@ import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
// Setup wallets and use WalletConfig for BitcoinJ wiring.
// Other like WalletConfig we are here always on the user thread. That is one reason why we do not
// merge WalletsSetup with WalletConfig to one class.
// Setup wallets and use WalletConfig for BitcoinJ wiring.
// Other like WalletConfig we are here always on the user thread. That is one reason why we do not
// merge WalletsSetup with WalletConfig to one class.
public class WalletsSetup {
private static final Logger log = LoggerFactory.getLogger(WalletsSetup.class);
@ -97,6 +98,7 @@ public class WalletsSetup {
UserAgent userAgent,
Preferences preferences,
Socks5ProxyProvider socks5ProxyProvider,
ProtobufferResolver protobufferResolver,
@Named(BtcOptionKeys.WALLET_DIR) File appDir,
@Named(BtcOptionKeys.SOCKS5_DISCOVER_MODE) String socks5DiscoverModeString) {
@ -111,7 +113,7 @@ public class WalletsSetup {
params = preferences.getBitcoinNetwork().getParameters();
walletDir = new File(appDir, "bitcoin");
storage = new Storage<>(walletDir);
storage = new Storage<>(walletDir, protobufferResolver);
Long nonce = storage.initAndGetPersistedWithFileName("BloomFilterNonce");
if (nonce != null) {
bloomFilterTweak = nonce;
@ -148,7 +150,7 @@ public class WalletsSetup {
walletConfig = new WalletConfig(params, socks5Proxy, walletDir, walletFileName, bsqWalletFileName) {
@Override
protected void onSetupCompleted() {
//We are here in the btcj thread Thread[ STARTING,5,main]
//We are here in the btcj thread Thread[ STARTING,5,main]
super.onSetupCompleted();
final PeerGroup peerGroup = walletConfig.peerGroup();
@ -286,28 +288,28 @@ public class WalletsSetup {
// by getting the real pub keys by intersections of several filters sent at each startup.
walletConfig.setBloomFilterTweak(bloomFilterTweak);
// Avoid the simple attack (see: https://jonasnick.github.io/blog/2015/02/12/privacy-in-bitcoinj/) due to the
// Avoid the simple attack (see: https://jonasnick.github.io/blog/2015/02/12/privacy-in-bitcoinj/) due to the
// default implementation using both pubkey and hash of pubkey. We have set a insertPubKey flag in BasicKeyChain to default false.
// Default only 266 keys are generated (2 * 100+33 -> 100 external and 100 internal keys + buffers of 30%). That would trigger new bloom filters when we are reaching
// Default only 266 keys are generated (2 * 100+33 -> 100 external and 100 internal keys + buffers of 30%). That would trigger new bloom filters when we are reaching
// the threshold. To avoid reaching the threshold we create much more keys which are unlikely to cause update of the
// filter for most users. With lookaheadSize of 500 we get 1333 keys (500*1.3=666 666 external and 666 internal keys) which should be enough for most users to
// filter for most users. With lookaheadSize of 500 we get 1333 keys (500*1.3=666 666 external and 666 internal keys) which should be enough for most users to
// never need to update a bloom filter, which would weaken privacy.
// As we use 2 wallets (BTC, BSQ) we generate 1333 + 266 keys in total.
walletConfig.setBtcWalletLookaheadSize(500);
walletConfig.setBsqWalletLookaheadSize(100);
// Calculation is derived from: https://www.reddit.com/r/Bitcoin/comments/2vrx6n/privacy_in_bitcoinj_android_wallet_multibit_hive/coknjuz
// No. of false positives (56M keys in the blockchain):
// No. of false positives (56M keys in the blockchain):
// First attempt for FP rate:
// FP rate = 0,0001; No. of false positives: 0,0001 * 56 000 000 = 5600
// We have 1333keys: 1333 / (5600 + 1333) = 0.19 -> 19 % probability that a pub key is in our wallet
// After tests I found out that the bandwidth consumption varies widely related to the generated filter.
// About 20- 40 MB for upload and 30-130 MB for download at first start up (spv chain).
// Afterwards its about 1 MB for upload and 20-80 MB for download.
// Probably better then a high FP rate would be to include foreign pubKeyHashes which are tested to not be used
// Probably better then a high FP rate would be to include foreign pubKeyHashes which are tested to not be used
// in many transactions. If we had a pool of 100 000 such keys (2 MB data dump) to random select 4000 we could mix it with our
// 1000 own keys and get a similar probability rate as with the current setup but less variation in bandwidth
// 1000 own keys and get a similar probability rate as with the current setup but less variation in bandwidth
// consumption.
// For now to reduce risks with high bandwidth consumption we reduce the FP rate by half.

View file

@ -20,6 +20,7 @@ package io.bisq.core.dao.vote;
import io.bisq.common.app.Version;
import io.bisq.common.persistance.Persistable;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
@ -27,12 +28,13 @@ import java.util.ArrayList;
import java.util.List;
@ToString
@EqualsAndHashCode
@EqualsAndHashCode(callSuper = true)
@Slf4j
public final class CompensationRequestVoteItemCollection extends VoteItem implements Persistable {
// That object is saved to disc. We need to take care of changes to not break deserialization.
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
@Getter
private final List<CompensationRequestVoteItem> compensationRequestVoteItems = new ArrayList<>();
/** constructor */
@ -40,10 +42,6 @@ public final class CompensationRequestVoteItemCollection extends VoteItem implem
super(votingType, null, null);
}
public List<CompensationRequestVoteItem> getCompensationRequestVoteItems() {
return compensationRequestVoteItems;
}
public List<CompensationRequestVoteItem> getCompensationRequestVoteItemsSortedByTxId() {
ArrayList<CompensationRequestVoteItem> list = new ArrayList<>(compensationRequestVoteItems);
list.sort((o1, o2) -> o2.compensationRequest.getCompensationRequestPayload().feeTxId.compareTo(o1.compensationRequest.getCompensationRequestPayload().feeTxId));

View file

@ -25,6 +25,8 @@ import io.bisq.common.app.Log;
import io.bisq.common.crypto.KeyRing;
import io.bisq.common.handlers.ErrorMessageHandler;
import io.bisq.common.handlers.ResultHandler;
import io.bisq.common.persistance.Msg;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.storage.Storage;
import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.wallet.BtcWalletService;
@ -96,6 +98,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
ClosedTradableManager closedTradableManager,
PriceFeedService priceFeedService,
Preferences preferences,
ProtobufferResolver protobufferResolver,
@Named(Storage.DIR_KEY) File storageDir) {
this.keyRing = keyRing;
this.user = user;
@ -106,7 +109,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
this.closedTradableManager = closedTradableManager;
this.preferences = preferences;
openOffersStorage = new Storage<>(storageDir);
openOffersStorage = new Storage<>(storageDir, protobufferResolver);
openOffers = new TradableList<>(openOffersStorage, "OpenOffers");
openOffers.forEach(e -> e.getOffer().setPriceFeedService(priceFeedService));
@ -207,7 +210,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
// but other peers have it already removed because of expired TTL.
// Those other not directly connected peers would not get the broadcast of the new offer, as the first
// connected peer (seed node) does nto broadcast if it has the data in the map.
// To update quickly to the whole network we repeat the republishOffers call after a few seconds when we
// To update quickly to the whole network we repeat the republishOffers call after a few seconds when we
// are better connected to the network. There is no guarantee that all peers will receive it but we have
// also our periodic timer, so after that longer interval the offer should be available to all peers.
if (retryRepublishOffersTimer == null)
@ -367,8 +370,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
// TODO mediators not impl yet
List<NodeAddress> acceptedArbitrators = user.getAcceptedArbitratorAddresses();
if (acceptedArbitrators != null && !acceptedArbitrators.isEmpty()) {
// Check also tradePrice to avoid failures after taker fee is paid caused by a too big difference
// in trade price between the peers. Also here poor connectivity might cause market price API connection
// Check also tradePrice to avoid failures after taker fee is paid caused by a too big difference
// in trade price between the peers. Also here poor connectivity might cause market price API connection
// losses and therefore an outdated market price.
try {
offer.checkTradePriceTolerance(message.takersTradePrice);
@ -441,9 +444,9 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
final OpenOffer openOffer = openOffersList.get(i);
UserThread.runAfterRandomDelay(() -> {
if (openOffers.contains(openOffer)) {
// The openOffer.getId().contains("_") check is because there was once a version
// The openOffer.getId().contains("_") check is because there was once a version
// where we encoded the version nr in the offer id with a "_" as separator.
// That caused several issues and was reverted. So if there are still old offers out with that
// That caused several issues and was reverted. So if there are still old offers out with that
// special offer ID format those must not be published as they cause failed taker attempts
// with lost taker fee.
String id = openOffer.getId();

View file

@ -29,7 +29,7 @@ import io.bisq.core.offer.messages.OfferAvailabilityResponse;
import io.bisq.core.offer.messages.OfferMsg;
import io.bisq.core.util.Validator;
import io.bisq.network.p2p.DecryptedDirectMessageListener;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -22,7 +22,7 @@ import io.bisq.common.app.Capabilities;
import io.bisq.common.app.Version;
import io.bisq.core.offer.AvailabilityResult;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.SupportedCapabilitiesMsg;
import javax.annotation.Nullable;

View file

@ -1,19 +1,24 @@
package io.bisq.core.p2p.network;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.protobuf.ByteString;
import io.bisq.common.crypto.PubKeyRing;
import io.bisq.common.crypto.SealedAndSigned;
import io.bisq.common.locale.CountryUtil;
import io.bisq.common.locale.CurrencyUtil;
import io.bisq.common.monetary.Price;
import io.bisq.common.persistance.Msg;
import io.bisq.common.persistance.Persistable;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.core.alert.Alert;
import io.bisq.core.alert.PrivateNotificationMsg;
import io.bisq.core.alert.PrivateNotificationPayload;
import io.bisq.core.arbitration.*;
import io.bisq.core.arbitration.messages.*;
import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.AddressEntryList;
import io.bisq.core.btc.data.RawTransactionInput;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.dao.compensation.CompensationRequestPayload;
import io.bisq.core.filter.Filter;
import io.bisq.core.filter.PaymentAccountFilter;
@ -27,10 +32,8 @@ import io.bisq.core.trade.messages.*;
import io.bisq.core.trade.statistics.TradeStatistics;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.CloseConnectionMsg;
import io.bisq.network.p2p.Msg;
import io.bisq.network.p2p.NodeAddress;
import io.bisq.network.p2p.PrefixedSealedAndSignedMsg;
import io.bisq.network.p2p.network.ProtobufferResolver;
import io.bisq.network.p2p.peers.getdata.messages.GetDataResponse;
import io.bisq.network.p2p.peers.getdata.messages.GetUpdatedDataRequest;
import io.bisq.network.p2p.peers.getdata.messages.PreliminaryGetDataRequest;
@ -48,15 +51,11 @@ import io.bisq.network.p2p.storage.payload.ProtectedMailboxStorageEntry;
import io.bisq.network.p2p.storage.payload.ProtectedStorageEntry;
import io.bisq.network.p2p.storage.payload.StoragePayload;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.output.WriterOutputStream;
import org.bitcoinj.core.Coin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.util.CollectionUtils;
import javax.inject.Inject;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.stream.Collectors;
@ -76,13 +75,10 @@ import static io.bisq.generated.protobuffer.PB.Envelope.MessageCase.*;
*/
@Slf4j
public class CoreProtobufferResolver implements ProtobufferResolver {
private BtcWalletService btcWalletService;
@Inject
public CoreProtobufferResolver(BtcWalletService btcWalletService) {
this.btcWalletService = btcWalletService;
}
private Provider<AddressEntryList> addressEntryList;
@Override
public Optional<Msg> fromProto(PB.Envelope envelope) {
if (Objects.isNull(envelope)) {
@ -95,18 +91,6 @@ public class CoreProtobufferResolver implements ProtobufferResolver {
log.debug("Convert protobuffer envelope: {}", envelope.getMessageCase());
log.trace("Convert protobuffer envelope: {}", envelope.toString());
}
StringWriter stringWriter = new StringWriter();
WriterOutputStream writerOutputStream = new WriterOutputStream(stringWriter);
try {
envelope.writeTo(writerOutputStream);
writerOutputStream.flush();
stringWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
// todo just for testing...
AddressEntry AddressEntry = new AddressEntry(null, null, null, null, null, btcWalletService);
Msg result = null;
switch (envelope.getMessageCase()) {
@ -869,4 +853,56 @@ public class CoreProtobufferResolver implements ProtobufferResolver {
.stream()
.map(ByteString::toByteArray).collect(Collectors.toList()));
}
//////////////////////////////// DISK /////////////////////////////////////
@Override
public Optional<Persistable> fromProto(PB.DiskEnvelope envelope) {
if (Objects.isNull(envelope)) {
log.warn("fromProtoBuf called with empty disk envelope.");
return Optional.empty();
}
log.debug("Convert protobuffer disk envelope: {}", envelope.getMessageCase());
Persistable result = null;
switch (envelope.getMessageCase()) {
case ADDRESS_ENTRY_LIST:
addToAddressEntryList(envelope);
result = addressEntryList.get();
break;
/*
case NAVIGATION:
result = getPing(envelope);
break;
case PERSISTED_PEERS:
result = getPing(envelope);
break;
case PREFERENCES:
result = getPing(envelope);
break;
case USER:
result = getPing(envelope);
break;
case PERSISTED_P2P_STORAGE_DATA:
result = getPing(envelope);
break;
case SEQUENCE_NUMBER_MAP:
result = getPing(envelope);
break;
*/
default:
log.warn("Unknown message case:{}:{}", envelope.getMessageCase());
}
return Optional.ofNullable(result);
}
private void addToAddressEntryList(PB.DiskEnvelope envelope) {
envelope.getAddressEntryList().getAddressEntryList().stream().map(addressEntry -> addressEntryList.get().addAddressEntry(
new AddressEntry(addressEntry.getPubKey().toByteArray(), addressEntry.getPubKeyHash().toByteArray(), addressEntry.getParamId(), AddressEntry.Context.valueOf(addressEntry.getContext().name()),
addressEntry.getOfferId(), Coin.valueOf(addressEntry.getCoinLockedInMultiSig().getValue()), addressEntryList.get().getKeyBagSupplier())));
}
}

View file

@ -24,6 +24,8 @@ import io.bisq.common.crypto.KeyRing;
import io.bisq.common.handlers.ErrorMessageHandler;
import io.bisq.common.handlers.FaultHandler;
import io.bisq.common.handlers.ResultHandler;
import io.bisq.common.persistance.Msg;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.storage.Storage;
import io.bisq.core.arbitration.ArbitratorManager;
import io.bisq.core.btc.AddressEntry;
@ -111,6 +113,7 @@ public class TradeManager {
PriceFeedService priceFeedService,
FilterManager filterManager,
TradeStatisticsManager tradeStatisticsManager,
ProtobufferResolver protobufferResolver,
@Named(Storage.DIR_KEY) File storageDir) {
this.user = user;
this.keyRing = keyRing;
@ -124,7 +127,7 @@ public class TradeManager {
this.filterManager = filterManager;
this.tradeStatisticsManager = tradeStatisticsManager;
tradableListStorage = new Storage<>(storageDir);
tradableListStorage = new Storage<>(storageDir, protobufferResolver);
trades = new TradableList<>(tradableListStorage, "PendingTrades");
trades.forEach(e -> e.getOffer().setPriceFeedService(priceFeedService));
@ -234,7 +237,7 @@ public class TradeManager {
// We only republish trades from last 10 days
// TODO check if needed at all. Don't want to remove it atm to not risk anything.
// But we could check which tradeStatistics we received from the seed nodes and
// But we could check which tradeStatistics we received from the seed nodes and
// only re-publish in case tradeStatistics are missing.
if ((new Date().getTime() - trade.getDate().getTime()) < TimeUnit.DAYS.toMillis(10)) {
long delay = 5000;
@ -493,4 +496,4 @@ public class TradeManager {
return getTrades().stream()
.filter(trade -> trade.isDepositPublished() && !trade.isPayoutPublished());
}
}
}

View file

@ -19,6 +19,7 @@ package io.bisq.core.trade.closed;
import com.google.inject.Inject;
import io.bisq.common.crypto.KeyRing;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.storage.Storage;
import io.bisq.core.offer.Offer;
import io.bisq.core.provider.price.PriceFeedService;
@ -38,9 +39,11 @@ public class ClosedTradableManager {
private final KeyRing keyRing;
@Inject
public ClosedTradableManager(KeyRing keyRing, PriceFeedService priceFeedService, @Named(Storage.DIR_KEY) File storageDir) {
public ClosedTradableManager(KeyRing keyRing, PriceFeedService priceFeedService,
ProtobufferResolver protobufferResolver,
@Named(Storage.DIR_KEY) File storageDir) {
this.keyRing = keyRing;
final Storage<TradableList<Tradable>> tradableListStorage = new Storage<>(storageDir);
final Storage<TradableList<Tradable>> tradableListStorage = new Storage<>(storageDir, protobufferResolver);
// The ClosedTrades object can become a few MB so we don't keep so many backups
tradableListStorage.setNumMaxBackupFiles(3);
this.closedTrades = new TradableList<>(tradableListStorage, "ClosedTrades");

View file

@ -19,6 +19,7 @@ package io.bisq.core.trade.failed;
import com.google.inject.Inject;
import io.bisq.common.crypto.KeyRing;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.storage.Storage;
import io.bisq.core.offer.Offer;
import io.bisq.core.provider.price.PriceFeedService;
@ -38,9 +39,11 @@ public class FailedTradesManager {
private final KeyRing keyRing;
@Inject
public FailedTradesManager(KeyRing keyRing, PriceFeedService priceFeedService, @Named(Storage.DIR_KEY) File storageDir) {
public FailedTradesManager(KeyRing keyRing, PriceFeedService priceFeedService,
ProtobufferResolver protobufferResolver,
@Named(Storage.DIR_KEY) File storageDir) {
this.keyRing = keyRing;
this.failedTrades = new TradableList<>(new Storage<>(storageDir), "FailedTrades");
this.failedTrades = new TradableList<>(new Storage<>(storageDir, protobufferResolver), "FailedTrades");
failedTrades.forEach(e -> e.getOffer().setPriceFeedService(priceFeedService));
}

View file

@ -21,7 +21,7 @@ import com.google.protobuf.ByteString;
import io.bisq.common.app.Version;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.MailboxMsg;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode;
import org.bouncycastle.util.encoders.Hex;

View file

@ -21,7 +21,7 @@ import com.google.protobuf.ByteString;
import io.bisq.common.app.Version;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.MailboxMsg;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode;

View file

@ -21,7 +21,7 @@ import com.google.protobuf.ByteString;
import io.bisq.common.app.Version;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.MailboxMsg;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode;

View file

@ -23,7 +23,7 @@ import io.bisq.common.crypto.PubKeyRing;
import io.bisq.core.btc.data.RawTransactionInput;
import io.bisq.core.payment.payload.PaymentAccountPayload;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode;
import org.bouncycastle.util.encoders.Hex;

View file

@ -21,7 +21,7 @@ import com.google.protobuf.ByteString;
import io.bisq.common.app.Version;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.MailboxMsg;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode;

View file

@ -24,7 +24,7 @@ import io.bisq.core.btc.data.RawTransactionInput;
import io.bisq.core.payment.payload.PaymentAccountPayload;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.MailboxMsg;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode;
import org.bouncycastle.util.encoders.Hex;

View file

@ -33,7 +33,7 @@ import io.bisq.core.trade.protocol.tasks.buyer_as_maker.BuyerAsMakerSignPayoutTx
import io.bisq.core.trade.protocol.tasks.maker.*;
import io.bisq.core.util.Validator;
import io.bisq.network.p2p.MailboxMsg;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.extern.slf4j.Slf4j;

View file

@ -33,7 +33,7 @@ import io.bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerCreatesDepos
import io.bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerSignAndPublishDepositTx;
import io.bisq.core.trade.protocol.tasks.taker.*;
import io.bisq.network.p2p.MailboxMsg;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.extern.slf4j.Slf4j;

View file

@ -88,8 +88,11 @@ public class ProcessModel implements Model, Serializable {
@Getter
@Setter
private List<NodeAddress> takerAcceptedArbitratorNodeAddresses;
@Getter
@Setter
private List<NodeAddress> takerAcceptedMediatorNodeAddresses;
// that is used to store temp. the peers address when we get an incoming message before the message is verified.
// After successful verified we copy that over to the trade.tradingPeerAddress
@Getter
@ -196,10 +199,6 @@ public class ProcessModel implements Model, Serializable {
return payoutTxSignature;
}
public void setPayoutTxSignature(byte[] payoutTxSignature) {
this.payoutTxSignature = payoutTxSignature;
}
@Override
public void persist() {
}
@ -212,62 +211,6 @@ public class ProcessModel implements Model, Serializable {
return keyRing.getPubKeyRing();
}
public KeyRing getKeyRing() {
return keyRing;
}
public void setTakerAcceptedArbitratorNodeAddresses(List<NodeAddress> takerAcceptedArbitratorNodeAddresses) {
this.takerAcceptedArbitratorNodeAddresses = takerAcceptedArbitratorNodeAddresses;
}
public List<NodeAddress> getTakerAcceptedArbitratorNodeAddresses() {
return takerAcceptedArbitratorNodeAddresses;
}
public void setTakerAcceptedMediatorNodeAddresses(List<NodeAddress> takerAcceptedMediatorNodeAddresses) {
this.takerAcceptedMediatorNodeAddresses = takerAcceptedMediatorNodeAddresses;
}
public List<NodeAddress> getTakerAcceptedMediatorNodeAddresses() {
return takerAcceptedMediatorNodeAddresses;
}
public void setTempTradingPeerNodeAddress(NodeAddress tempTradingPeerNodeAddress) {
this.tempTradingPeerNodeAddress = tempTradingPeerNodeAddress;
}
public NodeAddress getTempTradingPeerNodeAddress() {
return tempTradingPeerNodeAddress;
}
public ArbitratorManager getArbitratorManager() {
return arbitratorManager;
}
public void setPreparedDepositTx(byte[] preparedDepositTx) {
this.preparedDepositTx = preparedDepositTx;
}
public byte[] getPreparedDepositTx() {
return preparedDepositTx;
}
public void setRawTransactionInputs(ArrayList<RawTransactionInput> rawTransactionInputs) {
this.rawTransactionInputs = rawTransactionInputs;
}
public ArrayList<RawTransactionInput> getRawTransactionInputs() {
return rawTransactionInputs;
}
public void setChangeOutputValue(long changeOutputValue) {
this.changeOutputValue = changeOutputValue;
}
public long getChangeOutputValue() {
return changeOutputValue;
}
public void setChangeOutputAddress(String changeOutputAddress) {
this.changeOutputAddress = changeOutputAddress;
}

View file

@ -34,7 +34,7 @@ import io.bisq.core.trade.protocol.tasks.seller.SellerSignAndFinalizePayoutTx;
import io.bisq.core.trade.protocol.tasks.seller_as_maker.SellerAsMakerCreatesAndSignsDepositTx;
import io.bisq.core.util.Validator;
import io.bisq.network.p2p.MailboxMsg;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.extern.slf4j.Slf4j;

View file

@ -33,7 +33,7 @@ import io.bisq.core.trade.protocol.tasks.seller_as_taker.SellerAsTakerCreatesDep
import io.bisq.core.trade.protocol.tasks.seller_as_taker.SellerAsTakerSignAndPublishDepositTx;
import io.bisq.core.trade.protocol.tasks.taker.*;
import io.bisq.network.p2p.MailboxMsg;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import lombok.extern.slf4j.Slf4j;

View file

@ -26,7 +26,7 @@ import io.bisq.core.trade.TradeManager;
import io.bisq.core.trade.messages.TradeMsg;
import io.bisq.network.p2p.DecryptedDirectMessageListener;
import io.bisq.network.p2p.DecryptedMsgWithPubKey;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import javafx.beans.value.ChangeListener;
import lombok.extern.slf4j.Slf4j;

View file

@ -39,7 +39,7 @@ import io.bisq.gui.common.view.CachingViewLoader;
import io.bisq.gui.main.overlays.notifications.NotificationCenter;
import io.bisq.network.crypto.EncryptionServiceModule;
import io.bisq.network.p2p.P2PModule;
import io.bisq.network.p2p.network.ProtobufferResolver;
import io.bisq.common.persistance.ProtobufferResolver;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -21,6 +21,7 @@ import com.google.inject.Inject;
import io.bisq.common.locale.CryptoCurrency;
import io.bisq.common.locale.FiatCurrency;
import io.bisq.common.locale.TradeCurrency;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.core.offer.OpenOfferManager;
import io.bisq.core.payment.CryptoCurrencyAccount;
import io.bisq.core.payment.PaymentAccount;
@ -49,15 +50,17 @@ class AltCoinAccountsDataModel extends ActivatableDataModel {
final ObservableList<PaymentAccount> paymentAccounts = FXCollections.observableArrayList();
private final SetChangeListener<PaymentAccount> setChangeListener;
private final String accountsFileName = "AltcoinPaymentAccounts";
private final ProtobufferResolver protobufferResolver;
@Inject
public AltCoinAccountsDataModel(User user, Preferences preferences, OpenOfferManager openOfferManager,
TradeManager tradeManager, Stage stage) {
TradeManager tradeManager, Stage stage, ProtobufferResolver protobufferResolver) {
this.user = user;
this.preferences = preferences;
this.openOfferManager = openOfferManager;
this.tradeManager = tradeManager;
this.stage = stage;
this.protobufferResolver = protobufferResolver;
setChangeListener = change -> fillAndSortPaymentAccounts();
}
@ -126,10 +129,10 @@ class AltCoinAccountsDataModel extends ActivatableDataModel {
ArrayList<PaymentAccount> accounts = new ArrayList<>(user.getPaymentAccounts().stream()
.filter(paymentAccount -> paymentAccount instanceof CryptoCurrencyAccount)
.collect(Collectors.toList()));
GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage);
GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage, protobufferResolver);
}
public void importAccounts() {
GUIUtil.importAccounts(user, accountsFileName, preferences, stage);
GUIUtil.importAccounts(user, accountsFileName, preferences, stage, protobufferResolver);
}
}

View file

@ -21,6 +21,7 @@ import com.google.inject.Inject;
import io.bisq.common.locale.CryptoCurrency;
import io.bisq.common.locale.FiatCurrency;
import io.bisq.common.locale.TradeCurrency;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.core.offer.OpenOfferManager;
import io.bisq.core.payment.CryptoCurrencyAccount;
import io.bisq.core.payment.PaymentAccount;
@ -49,15 +50,17 @@ class FiatAccountsDataModel extends ActivatableDataModel {
final ObservableList<PaymentAccount> paymentAccounts = FXCollections.observableArrayList();
private final SetChangeListener<PaymentAccount> setChangeListener;
private final String accountsFileName = "FiatPaymentAccounts";
private final ProtobufferResolver protobufferResolver;
@Inject
public FiatAccountsDataModel(User user, Preferences preferences, OpenOfferManager openOfferManager,
TradeManager tradeManager, Stage stage) {
TradeManager tradeManager, Stage stage, ProtobufferResolver protobufferResolver) {
this.user = user;
this.preferences = preferences;
this.openOfferManager = openOfferManager;
this.tradeManager = tradeManager;
this.stage = stage;
this.protobufferResolver = protobufferResolver;
setChangeListener = change -> fillAndSortPaymentAccounts();
}
@ -127,10 +130,10 @@ class FiatAccountsDataModel extends ActivatableDataModel {
ArrayList<PaymentAccount> accounts = new ArrayList<>(user.getPaymentAccounts().stream()
.filter(paymentAccount -> !(paymentAccount instanceof CryptoCurrencyAccount))
.collect(Collectors.toList()));
GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage);
GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage, protobufferResolver);
}
public void importAccounts() {
GUIUtil.importAccounts(user, accountsFileName, preferences, stage);
GUIUtil.importAccounts(user, accountsFileName, preferences, stage, protobufferResolver);
}
}

View file

@ -40,7 +40,8 @@ import org.bitcoinj.crypto.KeyCrypterScrypt;
import javax.inject.Inject;
import static com.google.inject.internal.util.$Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkArgument;
@FxmlView
public class PasswordView extends ActivatableView<GridPane, Void> {

View file

@ -59,7 +59,7 @@ import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.concurrent.TimeUnit;
import static com.google.inject.internal.util.$Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkArgument;
import static io.bisq.gui.util.FormBuilder.*;
import static javafx.beans.binding.Bindings.createBooleanBinding;

View file

@ -26,6 +26,7 @@ import io.bisq.common.app.DevEnv;
import io.bisq.common.locale.CurrencyUtil;
import io.bisq.common.locale.Res;
import io.bisq.common.locale.TradeCurrency;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.storage.Storage;
import io.bisq.common.util.Utilities;
import io.bisq.core.payment.PaymentAccount;
@ -88,10 +89,11 @@ public class GUIUtil {
}
}
public static void exportAccounts(ArrayList<PaymentAccount> accounts, String fileName, Preferences preferences, Stage stage) {
public static void exportAccounts(ArrayList<PaymentAccount> accounts, String fileName,
Preferences preferences, Stage stage, ProtobufferResolver protobufferResolver) {
if (!accounts.isEmpty()) {
String directory = getDirectoryFromChooser(preferences, stage);
Storage<ArrayList<PaymentAccount>> paymentAccountsStorage = new Storage<>(new File(directory));
Storage<ArrayList<PaymentAccount>> paymentAccountsStorage = new Storage<>(new File(directory), protobufferResolver);
paymentAccountsStorage.initAndGetPersisted(accounts, fileName);
paymentAccountsStorage.queueUpForSave();
new Popup<>().feedback(Res.get("guiUtil.accountExport.savedToPath", Paths.get(directory, fileName).toAbsolutePath())).show();
@ -100,7 +102,8 @@ public class GUIUtil {
}
}
public static void importAccounts(User user, String fileName, Preferences preferences, Stage stage) {
public static void importAccounts(User user, String fileName, Preferences preferences, Stage stage,
ProtobufferResolver protobufferResolver) {
FileChooser fileChooser = new FileChooser();
fileChooser.setInitialDirectory(new File(preferences.getDirectoryChooserPath()));
fileChooser.setTitle(Res.get("guiUtil.accountExport.selectPath", fileName));
@ -110,7 +113,7 @@ public class GUIUtil {
if (Paths.get(path).getFileName().toString().equals(fileName)) {
String directory = Paths.get(path).getParent().toString();
preferences.setDirectoryChooserPath(directory);
Storage<ArrayList<PaymentAccount>> paymentAccountsStorage = new Storage<>(new File(directory));
Storage<ArrayList<PaymentAccount>> paymentAccountsStorage = new Storage<>(new File(directory), protobufferResolver);
ArrayList<PaymentAccount> persisted = paymentAccountsStorage.initAndGetPersistedWithFileName(fileName);
if (persisted != null) {
final StringBuilder msg = new StringBuilder();

View file

@ -18,7 +18,7 @@
package io.bisq.network.crypto;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import java.security.PublicKey;

View file

@ -22,8 +22,8 @@ import io.bisq.common.Marshaller;
import io.bisq.common.crypto.*;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.DecryptedMsgWithPubKey;
import io.bisq.network.p2p.Msg;
import io.bisq.network.p2p.network.ProtobufferResolver;
import io.bisq.common.persistance.Msg;
import io.bisq.common.persistance.ProtobufferResolver;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.SecretKey;

View file

@ -1,4 +1,6 @@
package io.bisq.network.p2p;
import io.bisq.common.persistance.Msg;
public interface AnonymousMsg extends Msg {
}

View file

@ -1,6 +1,7 @@
package io.bisq.network.p2p;
import io.bisq.common.app.Version;
import io.bisq.common.persistance.Msg;
import io.bisq.generated.protobuffer.PB;
public final class CloseConnectionMsg implements Msg {

View file

@ -18,6 +18,7 @@
package io.bisq.network.p2p;
import io.bisq.common.app.Version;
import io.bisq.common.persistance.Msg;
import io.bisq.common.persistance.Persistable;
import lombok.EqualsAndHashCode;

View file

@ -17,5 +17,7 @@
package io.bisq.network.p2p;
import io.bisq.common.persistance.Msg;
public interface DirectMsg extends Msg {
}

View file

@ -12,6 +12,8 @@ import io.bisq.common.app.Log;
import io.bisq.common.crypto.CryptoException;
import io.bisq.common.crypto.KeyRing;
import io.bisq.common.crypto.PubKeyRing;
import io.bisq.common.persistance.Msg;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.storage.FileUtil;
import io.bisq.common.storage.Storage;
import io.bisq.common.util.Utilities;
@ -198,11 +200,11 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
else
seedNodeAddresses = seedNodesRepository.getSeedNodeAddresses(useLocalhostForP2P, networkId);
peerManager = new PeerManager(networkNode, maxConnections, seedNodeAddresses, storageDir, clock);
peerManager = new PeerManager(networkNode, maxConnections, seedNodeAddresses, storageDir, clock, protobufferResolver);
broadcaster = new Broadcaster(networkNode, peerManager);
p2PDataStorage = new P2PDataStorage(broadcaster, networkNode, storageDir);
p2PDataStorage = new P2PDataStorage(broadcaster, networkNode, storageDir, protobufferResolver);
p2PDataStorage.addHashMapChangedListener(this);
requestDataManager = new RequestDataManager(networkNode, p2PDataStorage, peerManager, seedNodeAddresses, this);
@ -432,7 +434,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
try {
PrefixedSealedAndSignedMsg prefixedSealedAndSignedMessage = (PrefixedSealedAndSignedMsg) msg;
if (verifyAddressPrefixHash(prefixedSealedAndSignedMessage)) {
// We set connectionType to that connection to avoid that is get closed when
// We set connectionType to that connection to avoid that is get closed when
// we get too many connection attempts.
connection.setPeerType(Connection.PeerType.DIRECT_MSG_PEER);
@ -683,7 +685,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
@Override
public void onBroadcastFailed(String errorMessage) {
// TODO investigate why not sending sendMailboxMessageListener.onFault. Related probably
// TODO investigate why not sending sendMailboxMessageListener.onFault. Related probably
// to the logic from BroadcastHandler.sendToPeer
}
};
@ -712,7 +714,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
// We need to delay a bit to avoid that we remove our msg then get it from other peers again and reapply it again.
// If we delay the removal we have better chances that repeated network_messages we got from other peers are already filtered
// at the P2PService layer.
// Though we have to check in the client classes to not apply the same message again as there is no guarantee
// Though we have to check in the client classes to not apply the same message again as there is no guarantee
// when we would get a message again from the network.
UserThread.runAfter(() -> {
delayedRemoveEntryFromMailbox(decryptedMsgWithPubKey);

View file

@ -1,6 +1,8 @@
package io.bisq.network.p2p;
import io.bisq.common.persistance.Msg;
public interface SendersNodeAddressMsg extends Msg {
NodeAddress getSenderNodeAddress();
}

View file

@ -1,5 +1,7 @@
package io.bisq.network.p2p;
import io.bisq.common.persistance.Msg;
import javax.annotation.Nullable;
import java.util.ArrayList;

View file

@ -6,6 +6,8 @@ import com.google.common.util.concurrent.Uninterruptibles;
import io.bisq.common.UserThread;
import io.bisq.common.app.Log;
import io.bisq.common.app.Version;
import io.bisq.common.persistance.Msg;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.util.Tuple2;
import io.bisq.common.util.Utilities;
import io.bisq.generated.protobuffer.PB;

View file

@ -1,5 +1,7 @@
package io.bisq.network.p2p.network;
import io.bisq.common.persistance.ProtobufferResolver;
import java.net.Socket;
public class InboundConnection extends Connection {

View file

@ -8,6 +8,7 @@ import com.msopentech.thali.java.toronionproxy.JavaOnionProxyContext;
import com.msopentech.thali.java.toronionproxy.JavaOnionProxyManager;
import io.bisq.common.UserThread;
import io.bisq.common.app.Log;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.util.Utilities;
import io.bisq.network.p2p.NodeAddress;
import io.nucleo.net.HiddenServiceDescriptor;

View file

@ -1,6 +1,6 @@
package io.bisq.network.p2p.network;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
public interface MessageListener {
void onMessage(Msg msg, Connection connection);

View file

@ -4,8 +4,9 @@ import com.google.common.util.concurrent.*;
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
import io.bisq.common.UserThread;
import io.bisq.common.app.Log;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.util.Utilities;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;

View file

@ -1,5 +1,6 @@
package io.bisq.network.p2p.network;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.network.p2p.NodeAddress;
import java.net.Socket;

View file

@ -1,6 +1,7 @@
package io.bisq.network.p2p.network;
import io.bisq.common.app.Log;
import io.bisq.common.persistance.ProtobufferResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -1,7 +1,7 @@
package io.bisq.network.p2p.network;
import io.bisq.common.UserThread;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleIntegerProperty;

View file

@ -10,6 +10,7 @@ import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
import io.bisq.common.Timer;
import io.bisq.common.UserThread;
import io.bisq.common.app.Log;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.util.Utilities;
import io.bisq.network.p2p.NodeAddress;
import io.bisq.network.p2p.Utils;

View file

@ -4,6 +4,7 @@ import io.bisq.common.Clock;
import io.bisq.common.Timer;
import io.bisq.common.UserThread;
import io.bisq.common.app.Log;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.storage.Storage;
import io.bisq.network.p2p.NodeAddress;
import io.bisq.network.p2p.network.*;
@ -87,14 +88,14 @@ public class PeerManager implements ConnectionListener {
///////////////////////////////////////////////////////////////////////////////////////////
public PeerManager(NetworkNode networkNode, int maxConnections, Set<NodeAddress> seedNodeAddresses,
File storageDir, Clock clock) {
File storageDir, Clock clock, ProtobufferResolver protobufferResolver) {
setConnectionLimits(maxConnections);
this.networkNode = networkNode;
this.clock = clock;
// seedNodeAddresses can be empty (in case there is only 1 seed node, the seed node starting up has no other seed nodes)
this.seedNodeAddresses = new HashSet<>(seedNodeAddresses);
networkNode.addConnectionListener(this);
dbStorage = new Storage<>(storageDir);
dbStorage = new Storage<>(storageDir, protobufferResolver);
HashSet<Peer> persistedPeers = dbStorage.initAndGetPersistedWithFileName("PersistedPeers");
if (persistedPeers != null) {
log.debug("We have persisted reported peers. persistedPeers.size()=" + persistedPeers.size());

View file

@ -6,7 +6,7 @@ import com.google.common.util.concurrent.SettableFuture;
import io.bisq.common.Timer;
import io.bisq.common.UserThread;
import io.bisq.common.app.Log;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import io.bisq.network.p2p.network.CloseConnectionReason;
import io.bisq.network.p2p.network.Connection;

View file

@ -3,7 +3,7 @@ package io.bisq.network.p2p.peers.getdata;
import io.bisq.common.Timer;
import io.bisq.common.UserThread;
import io.bisq.common.app.Log;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import io.bisq.network.p2p.network.*;
import io.bisq.network.p2p.peers.PeerManager;

View file

@ -1,6 +1,6 @@
package io.bisq.network.p2p.peers.getdata.messages;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import java.util.Set;

View file

@ -6,7 +6,7 @@ import com.google.common.util.concurrent.SettableFuture;
import io.bisq.common.Timer;
import io.bisq.common.UserThread;
import io.bisq.common.app.Log;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.network.Connection;
import io.bisq.network.p2p.network.MessageListener;
import io.bisq.network.p2p.network.NetworkNode;

View file

@ -6,7 +6,7 @@ import com.google.common.util.concurrent.SettableFuture;
import io.bisq.common.Timer;
import io.bisq.common.UserThread;
import io.bisq.common.app.Log;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.network.*;
import io.bisq.network.p2p.peers.PeerManager;
import io.bisq.network.p2p.peers.keepalive.messages.Ping;

View file

@ -1,7 +1,7 @@
package io.bisq.network.p2p.peers.keepalive.messages;
import io.bisq.common.app.Version;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
public abstract class KeepAliveMsg implements Msg {
//TODO add serialVersionUID also in superclasses as changes would break compatibility

View file

@ -2,7 +2,7 @@ package io.bisq.network.p2p.peers.keepalive.messages;
import io.bisq.common.app.Version;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
public final class Ping extends KeepAliveMsg {
// That object is sent over the wire, so we need to take care of version compatibility.

View file

@ -2,7 +2,7 @@ package io.bisq.network.p2p.peers.keepalive.messages;
import io.bisq.common.app.Version;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
public final class Pong extends KeepAliveMsg {
// That object is sent over the wire, so we need to take care of version compatibility.

View file

@ -6,7 +6,7 @@ import com.google.common.util.concurrent.SettableFuture;
import io.bisq.common.Timer;
import io.bisq.common.UserThread;
import io.bisq.common.app.Log;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import io.bisq.network.p2p.network.CloseConnectionReason;
import io.bisq.network.p2p.network.Connection;

View file

@ -4,7 +4,7 @@ import com.google.common.base.Preconditions;
import io.bisq.common.Timer;
import io.bisq.common.UserThread;
import io.bisq.common.app.Log;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import io.bisq.network.p2p.network.*;
import io.bisq.network.p2p.peers.PeerManager;

View file

@ -1,7 +1,7 @@
package io.bisq.network.p2p.peers.peerexchange.messages;
import io.bisq.common.app.Version;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
abstract class PeerExchangeMsg implements Msg {
//TODO add serialVersionUID also in superclasses as changes would break compatibility

View file

@ -9,6 +9,7 @@ import io.bisq.common.app.Version;
import io.bisq.common.crypto.CryptoException;
import io.bisq.common.crypto.Sig;
import io.bisq.common.persistance.Persistable;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.storage.FileUtil;
import io.bisq.common.storage.ResourceNotFoundException;
import io.bisq.common.storage.Storage;
@ -16,7 +17,7 @@ import io.bisq.common.util.Tuple2;
import io.bisq.common.util.Utilities;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.crypto.EncryptionService;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import io.bisq.network.p2p.network.*;
import io.bisq.network.p2p.peers.BroadcastHandler;
@ -68,14 +69,14 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public P2PDataStorage(Broadcaster broadcaster, NetworkNode networkNode, File storageDir) {
public P2PDataStorage(Broadcaster broadcaster, NetworkNode networkNode, File storageDir, ProtobufferResolver protobufferResolver) {
this.broadcaster = broadcaster;
networkNode.addMessageListener(this);
networkNode.addConnectionListener(this);
sequenceNumberMapStorage = new Storage<>(storageDir);
persistedEntryMapStorage = new Storage<>(storageDir);
sequenceNumberMapStorage = new Storage<>(storageDir, protobufferResolver);
persistedEntryMapStorage = new Storage<>(storageDir, protobufferResolver);
init(storageDir);
}
@ -83,7 +84,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
private void init(File storageDir) {
sequenceNumberMapStorage.setNumMaxBackupFiles(5);
persistedEntryMapStorage.setNumMaxBackupFiles(1);
HashMap<ByteArray, MapValue> persistedSequenceNumberMap = sequenceNumberMapStorage.<HashMap<ByteArray, MapValue>>initAndGetPersistedWithFileName("SequenceNumberMap");
if (persistedSequenceNumberMap != null)
sequenceNumberMap = getPurgedSequenceNumberMap(persistedSequenceNumberMap);
@ -125,11 +126,11 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
public void onBootstrapComplete() {
removeExpiredEntriesTimer = UserThread.runPeriodically(() -> {
log.trace("removeExpiredEntries");
// The moment when an object becomes expired will not be synchronous in the network and we could
// The moment when an object becomes expired will not be synchronous in the network and we could
// get add network_messages after the object has expired. To avoid repeated additions of already expired
// object when we get it sent from new peers, we dont remove the sequence number from the map.
// That way an ADD message for an already expired data will fail because the sequence number
// is equal and not larger as expected.
// object when we get it sent from new peers, we dont remove the sequence number from the map.
// That way an ADD message for an already expired data will fail because the sequence number
// is equal and not larger as expected.
Map<ByteArray, ProtectedStorageEntry> temp = new HashMap<>(map);
Set<ProtectedStorageEntry> toRemoveSet = new HashSet<>();
temp.entrySet().stream()
@ -195,7 +196,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
RequiresOwnerIsOnlinePayload requiresOwnerIsOnlinePayload = (RequiresOwnerIsOnlinePayload) expirablePayload;
NodeAddress ownerNodeAddress = requiresOwnerIsOnlinePayload.getOwnerNodeAddress();
if (ownerNodeAddress.equals(connection.getPeersNodeAddressOptional().get())) {
// We have a RequiresLiveOwnerData data object with the node address of the
// We have a RequiresLiveOwnerData data object with the node address of the
// disconnected peer. We remove that data from our map.
// Check if we have the data (e.g. OfferPayload)
@ -210,15 +211,15 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
" / isIntended=" + closeConnectionReason.isIntended +
" / peer=" + (connection.getPeersNodeAddressOptional().isPresent() ? connection.getPeersNodeAddressOptional().get() : "PeersNode unknown"));
// We only set the data back by half of the TTL and remove the data only if is has
// expired after tha back dating.
// We might get connection drops which are not caused by the node going offline, so
// we give more tolerance with that approach, giving the node the change to
// We only set the data back by half of the TTL and remove the data only if is has
// expired after tha back dating.
// We might get connection drops which are not caused by the node going offline, so
// we give more tolerance with that approach, giving the node the change to
// refresh the TTL with a refresh message.
// We observed those issues during stress tests, but it might have been caused by the
// We observed those issues during stress tests, but it might have been caused by the
// test set up (many nodes/connections over 1 router)
// TODO investigate what causes the disconnections.
// Usually the are: SOCKET_TIMEOUT ,TERMINATED (EOFException)
// TODO investigate what causes the disconnections.
// Usually the are: SOCKET_TIMEOUT ,TERMINATED (EOFException)
protectedData.backDate();
if (protectedData.isExpired())
doRemoveProtectedExpirableData(protectedData, hashOfPayload);

View file

@ -2,7 +2,7 @@ package io.bisq.network.p2p.storage.messages;
import io.bisq.common.app.Version;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.storage.payload.ProtectedMailboxStorageEntry;
import io.bisq.network.p2p.storage.payload.ProtectedStorageEntry;

View file

@ -1,7 +1,7 @@
package io.bisq.network.p2p.storage.messages;
import io.bisq.common.app.Version;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
public abstract class BroadcastMsg implements Msg {
//TODO add serialVersionUID also in superclasses as changes would break compatibility

View file

@ -2,7 +2,7 @@ package io.bisq.network.p2p.storage.messages;
import io.bisq.common.app.Version;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.storage.payload.ProtectedStorageEntry;
public final class RemoveDataMsg extends BroadcastMsg {

View file

@ -2,7 +2,7 @@ package io.bisq.network.p2p.storage.messages;
import io.bisq.common.app.Version;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.storage.payload.ProtectedMailboxStorageEntry;

View file

@ -26,7 +26,7 @@ import io.bisq.common.crypto.PubKeyRing;
import io.bisq.common.storage.FileUtil;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.MailboxMsg;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.NodeAddress;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.After;

View file

@ -2,9 +2,11 @@ package io.bisq.network.p2p;
import io.bisq.common.Clock;
import io.bisq.common.crypto.KeyRing;
import io.bisq.common.persistance.Msg;
import io.bisq.common.persistance.Persistable;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.crypto.EncryptionService;
import io.bisq.network.p2p.network.ProtobufferResolver;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.network.p2p.seed.SeedNodesRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -176,6 +178,11 @@ public class TestUtils {
public Optional<Msg> fromProto(PB.Envelope envelope) {
return Optional.empty();
}
@Override
public Optional<Persistable> fromProto(PB.DiskEnvelope envelope) {
return null;
}
};
}
}

View file

@ -2,7 +2,7 @@ package io.bisq.network.p2p.mocks;
import io.bisq.common.app.Version;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.Msg;
import io.bisq.common.persistance.Msg;
import io.bisq.network.p2p.storage.payload.ExpirablePayload;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

View file

@ -3,6 +3,7 @@ package io.bisq.network.p2p.storage;
import io.bisq.common.crypto.CryptoException;
import io.bisq.common.crypto.KeyRing;
import io.bisq.common.crypto.KeyStorage;
import io.bisq.common.persistance.ProtobufferResolver;
import io.bisq.common.storage.FileUtil;
import io.bisq.network.crypto.EncryptionService;
import io.bisq.network.p2p.NodeAddress;
@ -43,6 +44,8 @@ public class P2PDataStorageTest {
Broadcaster broadcaster;
@Mocked
NetworkNode networkNode;
@Mocked
ProtobufferResolver protobufferResolver;
@Before
public void setup() throws InterruptedException, NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException, CryptoException, SignatureException, InvalidKeyException {
@ -62,7 +65,7 @@ public class P2PDataStorageTest {
keyRing2 = new KeyRing(new KeyStorage(dir2));
storageSignatureKeyPair2 = keyRing2.getSignatureKeyPair();
encryptionService2 = new EncryptionService(keyRing2, TestUtils.getProtobufferResolver());
dataStorage1 = new P2PDataStorage(broadcaster, networkNode, dir1);
dataStorage1 = new P2PDataStorage(broadcaster, networkNode, dir1, protobufferResolver);
}
@After
@ -75,7 +78,7 @@ public class P2PDataStorageTest {
}
//TODO CoreProtobufferResolver is not accessible here
// We should refactor it so that the classes themselves know how to deserialize
// We should refactor it so that the classes themselves know how to deserialize
// so we don't get dependencies from core objects here
/* @Test
@ -110,9 +113,9 @@ public class P2PDataStorageTest {
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
data.toProto().writeTo(byteOutputStream);
//TODO CoreProtobufferResolver is not accessible here
// We should refactor it so that the classes themselves know how to deserialize
// We should refactor it so that the classes themselves know how to deserialize
// so we don't get dependencies from core objects here
ProtectedStorageEntry protectedStorageEntry = ProtoBufferUtilities.getProtectedStorageEntry(PB.ProtectedStorageEntry.parseFrom(new ByteArrayInputStream(byteOutputStream.toByteArray())));
@ -122,7 +125,7 @@ public class P2PDataStorageTest {
}*/
//TODO CoreProtobufferResolver is not accessible here
// We should refactor it so that the classes themselves know how to deserialize
// We should refactor it so that the classes themselves know how to deserialize
// so we don't get dependencies from core objects here
/* @Test
public void testOfferRoundtrip() throws InvalidProtocolBufferException {
@ -199,4 +202,4 @@ public class P2PDataStorageTest {
byte[] hashOfDataAndSeqNr = Hash.getHash(new P2PDataStorage.DataAndSeqNrPair(entry.getStoragePayload(), entry.sequenceNumber));
return dataStorage1.checkSignature(entry.ownerPubKey, hashOfDataAndSeqNr, entry.signature);
}*/
}
}

10
pom.xml
View file

@ -122,7 +122,7 @@
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
<version>4.1.0</version>
</dependency>
<!--crypto-->
@ -165,7 +165,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
<version>19.0</version>
</dependency>
<dependency>
<groupId>org.fxmisc.easybind</groupId>
@ -239,6 +239,12 @@
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>3.2.0</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
<dependency>

View file

@ -36,7 +36,7 @@ import io.bisq.core.user.Preferences;
import io.bisq.core.user.User;
import io.bisq.network.crypto.EncryptionServiceModule;
import io.bisq.network.p2p.P2PModule;
import io.bisq.network.p2p.network.ProtobufferResolver;
import io.bisq.common.persistance.ProtobufferResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;

View file

@ -36,7 +36,7 @@ import io.bisq.core.user.Preferences;
import io.bisq.core.user.User;
import io.bisq.network.crypto.EncryptionServiceModule;
import io.bisq.network.p2p.P2PModule;
import io.bisq.network.p2p.network.ProtobufferResolver;
import io.bisq.common.persistance.ProtobufferResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;