Refactor accountagewitnessservice log helpers

Better filter for root signedwitnesses

Add debug keyboard shortcuts:
- ctrl+shift+L -> logSignedWitnesses
- ctrl+shift+S -> logSigners
This commit is contained in:
sqrrm 2020-04-28 11:17:09 +02:00
parent 8bf0f3350c
commit 7bd5969dcf
No known key found for this signature in database
GPG key ID: 45235F9EF87089EC
8 changed files with 200 additions and 98 deletions

View file

@ -334,6 +334,10 @@ public class Utilities {
return new KeyCodeCombination(keyCode, KeyCombination.ALT_DOWN).match(keyEvent);
}
public static boolean isCtrlShiftPressed(KeyCode keyCode, KeyEvent keyEvent) {
return new KeyCodeCombination(keyCode, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN).match(keyEvent);
}
public static byte[] concatenateByteArrays(byte[] array1, byte[] array2) {
return ArrayUtils.addAll(array1, array2);
}

View file

@ -334,10 +334,11 @@ public class SignedWitnessService {
.collect(Collectors.toSet());
}
public Set<SignedWitness> getOrphanSignedWitnessSet() {
public Set<SignedWitness> getRootSignedWitnessSet(boolean includeSignedByArbitrator) {
return signedWitnessMap.values().stream()
.filter(witness -> getSignedWitnessSetByOwnerPubKey(witness.getSignerPubKey(), new Stack<>()).isEmpty())
.filter(witness -> witness.getVerificationMethod() != SignedWitness.VerificationMethod.ARBITRATOR)
.filter(witness -> includeSignedByArbitrator ||
witness.getVerificationMethod() != SignedWitness.VerificationMethod.ARBITRATOR)
.collect(Collectors.toSet());
}

View file

@ -74,11 +74,8 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Stack;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -137,6 +134,8 @@ public class AccountAgeWitnessService {
private final SignedWitnessService signedWitnessService;
private final ChargeBackRisk chargeBackRisk;
private final FilterManager filterManager;
@Getter
private final AccountAgeWitnessUtils accountAgeWitnessUtils;
@Getter
private final Map<P2PDataStorage.ByteArray, AccountAgeWitness> accountAgeWitnessMap = new HashMap<>();
@ -163,6 +162,11 @@ public class AccountAgeWitnessService {
this.chargeBackRisk = chargeBackRisk;
this.filterManager = filterManager;
accountAgeWitnessUtils = new AccountAgeWitnessUtils(
this,
signedWitnessService,
keyRing);
// We need to add that early (before onAllServicesInitialized) as it will be used at startup.
appendOnlyDataStoreService.addService(accountAgeWitnessStorageService);
}
@ -231,7 +235,7 @@ public class AccountAgeWitnessService {
.orElse(null);
}
private byte[] getAccountInputDataWithSalt(PaymentAccountPayload paymentAccountPayload) {
byte[] getAccountInputDataWithSalt(PaymentAccountPayload paymentAccountPayload) {
return Utilities.concatenateByteArrays(paymentAccountPayload.getAgeWitnessInputData(), paymentAccountPayload.getSalt());
}
@ -243,7 +247,7 @@ public class AccountAgeWitnessService {
return new AccountAgeWitness(hash, new Date().getTime());
}
private Optional<AccountAgeWitness> findWitness(PaymentAccountPayload paymentAccountPayload,
Optional<AccountAgeWitness> findWitness(PaymentAccountPayload paymentAccountPayload,
PubKeyRing pubKeyRing) {
byte[] accountInputDataWithSalt = getAccountInputDataWithSalt(paymentAccountPayload);
byte[] hash = Hash.getSha256Ripemd160hash(Utilities.concatenateByteArrays(accountInputDataWithSalt,
@ -646,7 +650,7 @@ public class AccountAgeWitnessService {
ECKey key,
byte[] tradersPubKey,
long time) {
return signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, tradersPubKey,time);
return signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, tradersPubKey, time);
}
public void traderSignPeersAccountAgeWitness(Trade trade) {
@ -795,92 +799,9 @@ public class AccountAgeWitnessService {
}
public Set<AccountAgeWitness> getOrphanSignedWitnesses() {
var orphans = signedWitnessService.getOrphanSignedWitnessSet().stream()
return signedWitnessService.getRootSignedWitnessSet(false).stream()
.map(signedWitness -> getWitnessByHash(signedWitness.getAccountAgeWitnessHash()).orElse(null))
.filter(Objects::nonNull)
.collect(Collectors.toSet());
var orphanSigners = signedWitnessService.getOrphanSignedWitnessSet().stream()
.filter(Utilities.distinctByKey(w -> Utilities.bytesAsHexString(w.getSignerPubKey())))
.collect(Collectors.toSet());
var orphanAEW = signedWitnessService.getOrphanSignedWitnessSet();
log.debug("Orphaned signed account age witnesses:");
orphanSigners.forEach(w -> {
log.debug("{}: {}", w.getVerificationMethod().toString(),
Utilities.bytesAsHexString(Hash.getRipemd160hash(w.getSignerPubKey())));
logChild(w, " ", new HashSet<>());
});
return orphans;
}
private void logChild(SignedWitness sigWit, String initString, Set<SignedWitness> excluded) {
var allSig = signedWitnessService.getSignedWitnessMap();
log.debug("{}{}", initString, Utilities.bytesAsHexString(sigWit.getAccountAgeWitnessHash()));
allSig.values().forEach(w -> {
if (Arrays.equals(w.getSignerPubKey(), sigWit.getWitnessOwnerPubKey()) && !excluded.contains(w)) {
excluded.add(sigWit);
logChild(w, initString + " ", excluded);
}
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Debug logs
///////////////////////////////////////////////////////////////////////////////////////////
private String getWitnessDebugLog(PaymentAccountPayload paymentAccountPayload,
PubKeyRing pubKeyRing) {
Optional<AccountAgeWitness> accountAgeWitness = findWitness(paymentAccountPayload, pubKeyRing);
if (!accountAgeWitness.isPresent()) {
byte[] accountInputDataWithSalt = getAccountInputDataWithSalt(paymentAccountPayload);
byte[] hash = Hash.getSha256Ripemd160hash(Utilities.concatenateByteArrays(accountInputDataWithSalt,
pubKeyRing.getSignaturePubKeyBytes()));
return "No accountAgeWitness found for paymentAccountPayload with hash " + Utilities.bytesAsHexString(hash);
}
SignState signState = getSignState(accountAgeWitness.get());
return signState.name() + " " + signState.getPresentation() +
"\n" + accountAgeWitness.toString();
}
public void witnessDebugLog(Trade trade, @Nullable AccountAgeWitness myWitness) {
// Log to find why accounts sometimes don't get signed as expected
// TODO: Demote to debug or remove once account signing is working ok
checkNotNull(trade.getContract());
checkNotNull(trade.getContract().getBuyerPaymentAccountPayload());
boolean checkingSignTrade = true;
boolean isBuyer = trade.getContract().isMyRoleBuyer(keyRing.getPubKeyRing());
AccountAgeWitness witness = myWitness;
if (witness == null) {
witness = isBuyer ?
getMyWitness(trade.getContract().getBuyerPaymentAccountPayload()) :
getMyWitness(trade.getContract().getSellerPaymentAccountPayload());
checkingSignTrade = false;
}
boolean isSignWitnessTrade = accountIsSigner(witness) &&
!peerHasSignedWitness(trade) &&
tradeAmountIsSufficient(trade.getTradeAmount());
log.info("AccountSigning: " +
"\ntradeId: {}" +
"\nis buyer: {}" +
"\nbuyer account age witness info: {}" +
"\nseller account age witness info: {}" +
"\nchecking for sign trade: {}" +
"\nis myWitness signer: {}" +
"\npeer has signed witness: {}" +
"\ntrade amount: {}" +
"\ntrade amount is sufficient: {}" +
"\nisSignWitnessTrade: {}",
trade.getId(),
isBuyer,
getWitnessDebugLog(trade.getContract().getBuyerPaymentAccountPayload(),
trade.getContract().getBuyerPubKeyRing()),
getWitnessDebugLog(trade.getContract().getSellerPaymentAccountPayload(),
trade.getContract().getSellerPubKeyRing()),
checkingSignTrade, // Following cases added to use same logic as in seller signing check
accountIsSigner(witness),
peerHasSignedWitness(trade),
trade.getTradeAmount(),
tradeAmountIsSufficient(trade.getTradeAmount()),
isSignWitnessTrade);
}
}

View file

@ -0,0 +1,158 @@
/*
* 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.account.witness;
import bisq.core.account.sign.SignedWitness;
import bisq.core.account.sign.SignedWitnessService;
import bisq.core.payment.payload.PaymentAccountPayload;
import bisq.core.trade.Trade;
import bisq.network.p2p.storage.P2PDataStorage;
import bisq.common.crypto.Hash;
import bisq.common.crypto.KeyRing;
import bisq.common.crypto.PubKeyRing;
import bisq.common.util.Utilities;
import java.util.Arrays;
import java.util.Optional;
import java.util.Stack;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j
public class AccountAgeWitnessUtils {
private final AccountAgeWitnessService accountAgeWitnessService;
private final SignedWitnessService signedWitnessService;
private final KeyRing keyRing;
AccountAgeWitnessUtils(AccountAgeWitnessService accountAgeWitnessService,
SignedWitnessService signedWitnessService,
KeyRing keyRing) {
this.accountAgeWitnessService = accountAgeWitnessService;
this.signedWitnessService = signedWitnessService;
this.keyRing = keyRing;
}
// Log tree of signed witnesses
public void logSignedWitnesses() {
var orphanSigners = signedWitnessService.getRootSignedWitnessSet(false);
log.info("Orphaned signed account age witnesses:");
orphanSigners.forEach(w -> {
log.warn("{}: {}", w.getVerificationMethod().toString(),
Utilities.bytesAsHexString(Hash.getRipemd160hash(w.getSignerPubKey())));
logChild(w, " ", new Stack<>());
});
}
private void logChild(SignedWitness sigWit, String initString, Stack<P2PDataStorage.ByteArray> excluded) {
var allSig = signedWitnessService.getSignedWitnessMap();
log.warn("{}{}", initString, Utilities.bytesAsHexString(sigWit.getAccountAgeWitnessHash()));
allSig.values().forEach(w -> {
if (!excluded.contains(new P2PDataStorage.ByteArray(w.getWitnessOwnerPubKey())) &&
Arrays.equals(w.getSignerPubKey(), sigWit.getWitnessOwnerPubKey())) {
excluded.push(new P2PDataStorage.ByteArray(w.getWitnessOwnerPubKey()));
logChild(w, initString + " ", excluded);
excluded.pop();
}
});
}
// Log signers per
public void logSigners() {
log.info("signers per AEW");
var allSig = signedWitnessService.getSignedWitnessMap();
allSig.values().forEach(w -> {
log.info("AEW {}", Utilities.bytesAsHexString(w.getAccountAgeWitnessHash()));
allSig.values().forEach(ww -> {
if (Arrays.equals(w.getSignerPubKey(), ww.getWitnessOwnerPubKey())) {
log.info(" {}", Utilities.bytesAsHexString(ww.getAccountAgeWitnessHash()));
}
});
}
);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Debug logs
///////////////////////////////////////////////////////////////////////////////////////////
private String getWitnessDebugLog(PaymentAccountPayload paymentAccountPayload,
PubKeyRing pubKeyRing) {
Optional<AccountAgeWitness> accountAgeWitness =
accountAgeWitnessService.findWitness(paymentAccountPayload, pubKeyRing);
if (!accountAgeWitness.isPresent()) {
byte[] accountInputDataWithSalt =
accountAgeWitnessService.getAccountInputDataWithSalt(paymentAccountPayload);
byte[] hash = Hash.getSha256Ripemd160hash(Utilities.concatenateByteArrays(accountInputDataWithSalt,
pubKeyRing.getSignaturePubKeyBytes()));
return "No accountAgeWitness found for paymentAccountPayload with hash " + Utilities.bytesAsHexString(hash);
}
AccountAgeWitnessService.SignState signState =
accountAgeWitnessService.getSignState(accountAgeWitness.get());
return signState.name() + " " + signState.getPresentation() +
"\n" + accountAgeWitness.toString();
}
public void witnessDebugLog(Trade trade, @Nullable AccountAgeWitness myWitness) {
// Log to find why accounts sometimes don't get signed as expected
// TODO: Demote to debug or remove once account signing is working ok
checkNotNull(trade.getContract());
checkNotNull(trade.getContract().getBuyerPaymentAccountPayload());
boolean checkingSignTrade = true;
boolean isBuyer = trade.getContract().isMyRoleBuyer(keyRing.getPubKeyRing());
AccountAgeWitness witness = myWitness;
if (witness == null) {
witness = isBuyer ?
accountAgeWitnessService.getMyWitness(trade.getContract().getBuyerPaymentAccountPayload()) :
accountAgeWitnessService.getMyWitness(trade.getContract().getSellerPaymentAccountPayload());
checkingSignTrade = false;
}
boolean isSignWitnessTrade = accountAgeWitnessService.accountIsSigner(witness) &&
!accountAgeWitnessService.peerHasSignedWitness(trade) &&
accountAgeWitnessService.tradeAmountIsSufficient(trade.getTradeAmount());
log.info("AccountSigning: " +
"\ntradeId: {}" +
"\nis buyer: {}" +
"\nbuyer account age witness info: {}" +
"\nseller account age witness info: {}" +
"\nchecking for sign trade: {}" +
"\nis myWitness signer: {}" +
"\npeer has signed witness: {}" +
"\ntrade amount: {}" +
"\ntrade amount is sufficient: {}" +
"\nisSignWitnessTrade: {}",
trade.getId(),
isBuyer,
getWitnessDebugLog(trade.getContract().getBuyerPaymentAccountPayload(),
trade.getContract().getBuyerPubKeyRing()),
getWitnessDebugLog(trade.getContract().getSellerPaymentAccountPayload(),
trade.getContract().getSellerPubKeyRing()),
checkingSignTrade, // Following cases added to use same logic as in seller signing check
accountAgeWitnessService.accountIsSigner(witness),
accountAgeWitnessService.peerHasSignedWitness(trade),
trade.getTradeAmount(),
accountAgeWitnessService.tradeAmountIsSufficient(trade.getTradeAmount()),
isSignWitnessTrade);
}
}

View file

@ -358,6 +358,6 @@ public class ProcessModel implements Model, PersistablePayload {
}
void logTrade(Trade trade) {
accountAgeWitnessService.witnessDebugLog(trade, null);
accountAgeWitnessService.getAccountAgeWitnessUtils().witnessDebugLog(trade, null);
}
}

View file

@ -324,11 +324,11 @@ public class AccountAgeWitnessServiceTest {
assertFalse(signedWitnessService.isSignedAccountAgeWitness(aew2));
// Sign dummy AccountAgeWitness using signer key from SW_1
assertEquals(signedWitnessService.getOrphanSignedWitnessSet().size(), 1);
assertEquals(signedWitnessService.getRootSignedWitnessSet().size(), 1);
// TODO: move this to accountagewitnessservice
@SuppressWarnings("OptionalGetWithoutIsPresent")
var orphanedSignedWitness = signedWitnessService.getOrphanSignedWitnessSet().stream().findAny().get();
var orphanedSignedWitness = signedWitnessService.getRootSignedWitnessSet().stream().findAny().get();
var dummyAccountAgeWitnessHash = Hash.getRipemd160hash(orphanedSignedWitness.getSignerPubKey());
var dummyAEW = new AccountAgeWitness(dummyAccountAgeWitnessHash,
orphanedSignedWitness.getDate() -

View file

@ -16,6 +16,8 @@ import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.common.UserThread;
import bisq.common.app.DevEnv;
import bisq.common.util.Utilities;
import org.apache.commons.lang3.StringUtils;
@ -26,10 +28,14 @@ import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.beans.value.ChangeListener;
import javafx.event.EventHandler;
import javafx.collections.ObservableList;
import javafx.util.Callback;
@ -41,8 +47,8 @@ public abstract class PaymentAccountsView<R extends Node, M extends ActivatableW
protected ListView<PaymentAccount> paymentAccountsListView;
private ChangeListener<PaymentAccount> paymentAccountChangeListener;
protected Button addAccountButton, exportButton, importButton;
SignedWitnessService signedWitnessService;
protected AccountAgeWitnessService accountAgeWitnessService;
private EventHandler<KeyEvent> keyEventEventHandler;
public PaymentAccountsView(M model, AccountAgeWitnessService accountAgeWitnessService) {
super(model);
@ -51,6 +57,14 @@ public abstract class PaymentAccountsView<R extends Node, M extends ActivatableW
@Override
public void initialize() {
keyEventEventHandler = event -> {
if (Utilities.isCtrlShiftPressed(KeyCode.L, event)) {
accountAgeWitnessService.getAccountAgeWitnessUtils().logSignedWitnesses();
} else if (Utilities.isCtrlShiftPressed(KeyCode.S, event)) {
accountAgeWitnessService.getAccountAgeWitnessUtils().logSigners();
}
};
buildForm();
paymentAccountChangeListener = (observable, oldValue, newValue) -> {
if (newValue != null)
@ -68,6 +82,8 @@ public abstract class PaymentAccountsView<R extends Node, M extends ActivatableW
addAccountButton.setOnAction(event -> addNewAccount());
exportButton.setOnAction(event -> exportAccounts());
importButton.setOnAction(event -> importAccounts());
if (root.getScene() != null)
root.getScene().addEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
}
@Override
@ -76,6 +92,8 @@ public abstract class PaymentAccountsView<R extends Node, M extends ActivatableW
addAccountButton.setOnAction(null);
exportButton.setOnAction(null);
importButton.setOnAction(null);
if (root.getScene() != null)
root.getScene().removeEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
}
protected void onDeleteAccount(PaymentAccount paymentAccount) {

View file

@ -380,7 +380,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
checkNotNull(trade.getOffer(), "offer must not be null");
AccountAgeWitness myWitness = accountAgeWitnessService.getMyWitness(dataModel.getSellersPaymentAccountPayload());
accountAgeWitnessService.witnessDebugLog(trade, myWitness);
accountAgeWitnessService.getAccountAgeWitnessUtils().witnessDebugLog(trade, myWitness);
return accountAgeWitnessService.accountIsSigner(myWitness) &&
!accountAgeWitnessService.peerHasSignedWitness(trade) &&