Skip to content

Commit

Permalink
Merge pull request #5953 from jmacxx/logs_upload
Browse files Browse the repository at this point in the history
Feature enabling log file upload to mediators
  • Loading branch information
bisq-github-admin-3 authored Jan 12, 2022
2 parents 310b9a9 + d9ad531 commit ddead5e
Show file tree
Hide file tree
Showing 19 changed files with 1,266 additions and 36 deletions.
14 changes: 13 additions & 1 deletion common/src/main/java/bisq/common/file/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.nio.file.Paths;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -43,6 +44,7 @@
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -128,7 +130,7 @@ public static void deleteDirectory(File file,
try {
deleteFileIfExists(file, ignoreLockedFiles);
} catch (Throwable t) {
log.error("Could not delete file. Error=" + t.toString());
log.error("Could not delete file. Error=" + t);
throw new IOException(t);
}
}
Expand Down Expand Up @@ -259,4 +261,14 @@ public static void removeAndBackupFile(File dbDir, File storageFile, String file
renameFile(storageFile, corruptedFile);
}
}

public static boolean doesFileContainKeyword(File file, String keyword) throws FileNotFoundException {
Scanner s = new Scanner(file);
while (s.hasNextLine()) {
if (s.nextLine().contains(keyword)) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
import bisq.network.p2p.AckMessage;
import bisq.network.p2p.BundleOfEnvelopes;
import bisq.network.p2p.CloseConnectionMessage;
import bisq.network.p2p.FileTransferPart;
import bisq.network.p2p.PrefixedSealedAndSignedMessage;
import bisq.network.p2p.peers.getdata.messages.GetDataResponse;
import bisq.network.p2p.peers.getdata.messages.GetUpdatedDataRequest;
Expand Down Expand Up @@ -131,6 +132,8 @@ public NetworkEnvelope fromProto(protobuf.NetworkEnvelope proto) throws Protobuf
return Ping.fromProto(proto.getPing(), messageVersion);
case PONG:
return Pong.fromProto(proto.getPong(), messageVersion);
case FILE_TRANSFER_PART:
return FileTransferPart.fromProto(proto.getFileTransferPart(), messageVersion);

case OFFER_AVAILABILITY_REQUEST:
return OfferAvailabilityRequest.fromProto(proto.getOfferAvailabilityRequest(), messageVersion);
Expand Down
27 changes: 27 additions & 0 deletions core/src/main/java/bisq/core/support/dispute/Dispute.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,15 @@
import bisq.core.locale.Res;
import bisq.core.proto.CoreProtoResolver;
import bisq.core.support.SupportType;
import bisq.core.support.dispute.mediation.FileTransferReceiver;
import bisq.core.support.dispute.mediation.FileTransferSender;
import bisq.core.support.dispute.mediation.FileTransferSession;
import bisq.core.support.messages.ChatMessage;
import bisq.core.trade.model.bisq_v1.Contract;

import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.network.NetworkNode;

import bisq.common.crypto.PubKeyRing;
import bisq.common.proto.ProtoUtil;
import bisq.common.proto.network.NetworkPayload;
Expand All @@ -46,6 +52,8 @@
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import java.io.IOException;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
Expand Down Expand Up @@ -154,6 +162,20 @@ public static protobuf.Dispute.State toProtoMessage(Dispute.State state) {
private transient final BooleanProperty isClosedProperty = new SimpleBooleanProperty();
private transient final IntegerProperty badgeCountProperty = new SimpleIntegerProperty();

private transient FileTransferReceiver fileTransferSession = null;

public FileTransferReceiver createOrGetFileTransferReceiver(NetworkNode networkNode, NodeAddress peerNodeAddress, FileTransferSession.FtpCallback callback) throws IOException {
// the receiver stores its state temporarily here in the dispute
// this method gets called to retrieve the session each time a part of the log files is received
if (fileTransferSession == null) {
fileTransferSession = new FileTransferReceiver(networkNode, peerNodeAddress, this.tradeId, this.traderId, this.getRoleStringForLogFile(), callback);
}
return fileTransferSession;
}

public FileTransferSender createFileTransferSender(NetworkNode networkNode, NodeAddress peerNodeAddress, FileTransferSession.FtpCallback callback) {
return new FileTransferSender(networkNode, peerNodeAddress, this.tradeId, this.traderId, this.getRoleStringForLogFile(), callback);
}

///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
Expand Down Expand Up @@ -441,6 +463,11 @@ public String getRoleString() {
}
}

public String getRoleStringForLogFile() {
return (disputeOpenerIsBuyer ? "BUYER" : "SELLER") + "_"
+ (disputeOpenerIsMaker ? "MAKER" : "TAKER");
}

@Override
public String toString() {
return "Dispute{" +
Expand Down
35 changes: 24 additions & 11 deletions core/src/main/java/bisq/core/support/dispute/DisputeManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public void requestPersistence() {
@Override
public NodeAddress getPeerNodeAddress(ChatMessage message) {
Optional<Dispute> disputeOptional = findDispute(message);
if (!disputeOptional.isPresent()) {
if (disputeOptional.isEmpty()) {
log.warn("Could not find dispute for tradeId = {} traderId = {}",
message.getTradeId(), message.getTraderId());
return null;
Expand All @@ -156,7 +156,7 @@ public NodeAddress getPeerNodeAddress(ChatMessage message) {
@Override
public PubKeyRing getPeerPubKeyRing(ChatMessage message) {
Optional<Dispute> disputeOptional = findDispute(message);
if (!disputeOptional.isPresent()) {
if (disputeOptional.isEmpty()) {
log.warn("Could not find dispute for tradeId = {} traderId = {}",
message.getTradeId(), message.getTraderId());
return null;
Expand Down Expand Up @@ -325,7 +325,7 @@ protected void onOpenNewDisputeMessage(OpenNewDisputeMessage openNewDisputeMessa
if (isAgent(dispute)) {
if (!disputeList.contains(dispute)) {
Optional<Dispute> storedDisputeOptional = findDispute(dispute);
if (!storedDisputeOptional.isPresent()) {
if (storedDisputeOptional.isEmpty()) {
disputeList.add(dispute);
sendPeerOpenedDisputeMessage(dispute, contract, peersPubKeyRing);
} else {
Expand Down Expand Up @@ -378,7 +378,7 @@ protected void onPeerOpenedDisputeMessage(PeerOpenedDisputeMessage peerOpenedDis
Dispute dispute = peerOpenedDisputeMessage.getDispute();

Optional<Trade> optionalTrade = tradeManager.getTradeById(dispute.getTradeId());
if (!optionalTrade.isPresent()) {
if (optionalTrade.isEmpty()) {
return;
}

Expand All @@ -399,11 +399,10 @@ protected void onPeerOpenedDisputeMessage(PeerOpenedDisputeMessage peerOpenedDis
if (!isAgent(dispute)) {
if (!disputeList.contains(dispute)) {
Optional<Dispute> storedDisputeOptional = findDispute(dispute);
if (!storedDisputeOptional.isPresent()) {
if (storedDisputeOptional.isEmpty()) {
disputeList.add(dispute);
trade.setDisputeState(getDisputeStateStartedByPeer());
tradeManager.requestPersistence();
errorMessage = null;
} else {
// valid case if both have opened a dispute and agent was not online.
log.debug("We got a dispute already open for that trade and trading peer. TradeId = {}",
Expand Down Expand Up @@ -452,7 +451,7 @@ public void sendOpenNewDisputeMessage(Dispute dispute,
}

Optional<Dispute> storedDisputeOptional = findDispute(dispute);
if (!storedDisputeOptional.isPresent() || reOpen) {
if (storedDisputeOptional.isEmpty() || reOpen) {
String disputeInfo = getDisputeInfo(dispute);
String disputeMessage = getDisputeIntroForDisputeCreator(disputeInfo);
String sysMsg = dispute.isSupportTicket() ?
Expand Down Expand Up @@ -794,7 +793,7 @@ private Tuple2<NodeAddress, PubKeyRing> getNodeAddressPubKeyRingTuple(Dispute di
return new Tuple2<>(peerNodeAddress, receiverPubKeyRing);
}

private boolean isAgent(Dispute dispute) {
public boolean isAgent(Dispute dispute) {
return pubKeyRing.equals(dispute.getAgentPubKeyRing());
}

Expand All @@ -812,7 +811,7 @@ private Optional<Dispute> findDispute(ChatMessage message) {
return findDispute(message.getTradeId(), message.getTraderId());
}

private Optional<Dispute> findDispute(String tradeId, int traderId) {
protected Optional<Dispute> findDispute(String tradeId, int traderId) {
T disputeList = getDisputeList();
if (disputeList == null) {
log.warn("disputes is null");
Expand All @@ -836,7 +835,7 @@ public Optional<Dispute> findDispute(String tradeId) {

public Optional<Trade> findTrade(Dispute dispute) {
Optional<Trade> retVal = tradeManager.getTradeById(dispute.getTradeId());
if (!retVal.isPresent()) {
if (retVal.isEmpty()) {
retVal = closedTradableManager.getClosedTrades().stream().filter(e -> e.getId().equals(dispute.getTradeId())).findFirst();
}
return retVal;
Expand Down Expand Up @@ -873,6 +872,20 @@ public void addMediationReOpenedMessage(Dispute dispute, boolean senderIsTrader)
requestPersistence();
}

protected void addMediationLogsReceivedMessage(Dispute dispute, String logsIdentifier) {
String logsReceivedMessage = Res.get("support.mediatorReceivedLogs", logsIdentifier);
ChatMessage chatMessage = new ChatMessage(
getSupportType(),
dispute.getTradeId(),
pubKeyRing.hashCode(),
false,
logsReceivedMessage,
p2PService.getAddress());
chatMessage.setSystemMessage(true);
dispute.addAndPersistChatMessage(chatMessage);
requestPersistence();
}

// If price was going down between take offer time and open dispute time the buyer has an incentive to
// not send the payment but to try to make a new trade with the better price. We risks to lose part of the
// security deposit (in mediation we will always get back 0.003 BTC to keep some incentive to accept mediated
Expand Down Expand Up @@ -958,7 +971,7 @@ private Price getPrice(String currencyCode) {
long roundedToLong = MathUtils.roundDoubleToLong(scaled);
return Price.valueOf(currencyCode, roundedToLong);
} catch (Exception e) {
log.error("Exception at getPrice / parseToFiat: " + e.toString());
log.error("Exception at getPrice / parseToFiat: " + e);
return null;
}
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* This file is part of Bisq.
*
* Bisq 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.
*
* Bisq 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 Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.core.support.dispute.mediation;

import bisq.network.p2p.AckMessage;
import bisq.network.p2p.AckMessageSourceType;
import bisq.network.p2p.FileTransferPart;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.network.NetworkNode;

import bisq.common.UserThread;
import bisq.common.config.Config;
import bisq.common.util.Utilities;

import java.nio.file.FileSystems;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;

import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;

@Slf4j
public class FileTransferReceiver extends FileTransferSession {
protected final String zipFilePath;

public FileTransferReceiver(NetworkNode networkNode, NodeAddress peerNodeAddress,
String tradeId, int traderId, String traderRole, @Nullable FileTransferSession.FtpCallback callback) throws IOException {
super(networkNode, peerNodeAddress, tradeId, traderId, traderRole, callback);
zipFilePath = ensureReceivingDirectoryExists().getAbsolutePath() + FileSystems.getDefault().getSeparator() + zipId + ".zip";
}

public void processFilePartReceived(FileTransferPart ftp) {
checkpointLastActivity();
// check that the supplied sequence number is in line with what we are expecting
if (currentBlockSeqNum < 0) {
// we have not yet started receiving a file, validate this ftp packet as the initiation request
initReceiveSession(ftp.uid, ftp.seqNumOrFileLength);
} else if (currentBlockSeqNum == ftp.seqNumOrFileLength) {
// we are in the middle of receiving a file; add the block of data to the file
processReceivedBlock(ftp, networkNode, peerNodeAddress);
} else {
log.error("ftp sequence num mismatch, expected {} received {}", currentBlockSeqNum, ftp.seqNumOrFileLength);
resetSession(); // aborts the file transfer
}
}

public void initReceiveSession(String uid, long expectedFileBytes) {
networkNode.addMessageListener(this);
this.expectedFileLength = expectedFileBytes;
fileOffsetBytes = 0;
currentBlockSeqNum = 0;
initSessionTimer();
log.info("Received a start file transfer request, tradeId={}, traderId={}, size={}", fullTradeId, traderId, expectedFileBytes);
log.info("New file will be written to {}", zipFilePath);
UserThread.execute(() -> ackReceivedPart(uid, networkNode, peerNodeAddress));
}

private void processReceivedBlock(FileTransferPart ftp, NetworkNode networkNode, NodeAddress peerNodeAddress) {
try {
RandomAccessFile file = new RandomAccessFile(zipFilePath, "rwd");
file.seek(fileOffsetBytes);
file.write(ftp.messageData.toByteArray(), 0, ftp.messageData.size());
fileOffsetBytes = fileOffsetBytes + ftp.messageData.size();
log.info("Sequence number {} for {}, received data {} / {}",
ftp.seqNumOrFileLength, Utilities.getShortId(ftp.tradeId), fileOffsetBytes, expectedFileLength);
currentBlockSeqNum++;
UserThread.runAfter(() -> {
ackReceivedPart(ftp.uid, networkNode, peerNodeAddress);
if (fileOffsetBytes >= expectedFileLength) {
log.info("Success! We have reached the EOF, received {} expected {}", fileOffsetBytes, expectedFileLength);
ftpCallback.ifPresent(c -> c.onFtpComplete(this));
resetSession();
}
}, 100, TimeUnit.MILLISECONDS);
} catch (IOException e) {
log.error(e.toString());
e.printStackTrace();
}
}

private void ackReceivedPart(String uid, NetworkNode networkNode, NodeAddress peerNodeAddress) {
AckMessage ackMessage = new AckMessage(peerNodeAddress,
AckMessageSourceType.LOG_TRANSFER,
FileTransferPart.class.getSimpleName(),
uid,
Utilities.getShortId(fullTradeId),
true, // result
null); // errorMessage
log.info("Send AckMessage for {} to peer {}. id={}, uid={}",
ackMessage.getSourceMsgClassName(), peerNodeAddress, ackMessage.getSourceId(), ackMessage.getSourceUid());
sendMessage(ackMessage, networkNode, peerNodeAddress);
}

private static File ensureReceivingDirectoryExists() throws IOException {
File directory = new File(Config.appDataDir() + "/clientLogs");
if (!directory.exists() && !directory.mkdirs()) {
log.error("Could not create directory {}", directory.getAbsolutePath());
throw new IOException("Could not create directory: " + directory.getAbsolutePath());
}
return directory;
}
}
Loading

0 comments on commit ddead5e

Please sign in to comment.