Add export feature for signed account age witness

This commit is contained in:
chimp1984 2022-07-31 14:40:18 +02:00
parent 2b6dd187c6
commit ef81bde7f2
No known key found for this signature in database
GPG Key ID: 9801B4EC591F90E3
4 changed files with 127 additions and 21 deletions

View File

@ -381,6 +381,15 @@ public class AccountAgeWitnessService {
} }
} }
public long getWitnessSignDate(AccountAgeWitness accountAgeWitness) {
List<Long> dates = signedWitnessService.getVerifiedWitnessDateList(accountAgeWitness);
if (dates.isEmpty()) {
return -1L;
} else {
return dates.get(0);
}
}
// Return -1 if not signed // Return -1 if not signed
public long getWitnessSignAge(Offer offer, Date now) { public long getWitnessSignAge(Offer offer, Date now) {
return findWitness(offer) return findWitness(offer)
@ -951,4 +960,8 @@ public class AccountAgeWitnessService {
return null; return null;
} }
} }
public boolean isFilteredWitness(AccountAgeWitness accountAgeWitness) {
return signedWitnessService.isFilteredWitness(accountAgeWitness);
}
} }

View File

@ -26,6 +26,7 @@ import bisq.core.util.JsonUtil;
import bisq.network.p2p.storage.P2PDataStorage; import bisq.network.p2p.storage.P2PDataStorage;
import bisq.common.app.DevEnv;
import bisq.common.crypto.CryptoException; import bisq.common.crypto.CryptoException;
import bisq.common.crypto.Hash; import bisq.common.crypto.Hash;
import bisq.common.crypto.KeyRing; import bisq.common.crypto.KeyRing;
@ -41,11 +42,13 @@ import java.util.Base64;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
import java.util.Stack; import java.util.Stack;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j @Slf4j
@ -197,13 +200,37 @@ public class AccountAgeWitnessUtils {
} }
} }
public static Optional<String> exportAccount(AccountAgeWitnessService accountAgeWitnessService, static class SignedWitnessDto {
PaymentAccount account, private final String profileId;
KeyRing keyRing, private final String hashAsHex;
String profileId) { private final long accountAgeWitnessDate;
private final long witnessSignDate;
private final String pubKeyBase64;
private final String signatureBase64;
public SignedWitnessDto(String profileId,
String hashAsHex,
long accountAgeWitnessDate,
long witnessSignDate,
String pubKeyBase64,
String signatureBase64) {
this.profileId = profileId;
this.hashAsHex = hashAsHex;
this.accountAgeWitnessDate = accountAgeWitnessDate;
this.witnessSignDate = witnessSignDate;
this.pubKeyBase64 = pubKeyBase64;
this.signatureBase64 = signatureBase64;
}
}
public static Optional<String> signAccountAgeAndBisq2ProfileId(AccountAgeWitnessService accountAgeWitnessService,
PaymentAccount account,
KeyRing keyRing,
String profileId) {
return accountAgeWitnessService.findWitness(account.getPaymentAccountPayload(), keyRing.getPubKeyRing()) return accountAgeWitnessService.findWitness(account.getPaymentAccountPayload(), keyRing.getPubKeyRing())
.map(accountAgeWitness -> { .map(accountAgeWitness -> {
try { try {
checkArgument(!accountAgeWitnessService.isFilteredWitness(accountAgeWitness), "Invalid account age witness");
String hashAsHex = Hex.encode(accountAgeWitness.getHash()); String hashAsHex = Hex.encode(accountAgeWitness.getHash());
String message = profileId + hashAsHex + accountAgeWitness.getDate(); String message = profileId + hashAsHex + accountAgeWitness.getDate();
KeyPair signatureKeyPair = keyRing.getSignatureKeyPair(); KeyPair signatureKeyPair = keyRing.getSignatureKeyPair();
@ -220,4 +247,36 @@ public class AccountAgeWitnessUtils {
} }
}); });
} }
public static Optional<String> signSignedWitnessAndBisq2ProfileId(AccountAgeWitnessService accountAgeWitnessService,
PaymentAccount account,
KeyRing keyRing,
String profileId) {
return accountAgeWitnessService.findWitness(account.getPaymentAccountPayload(), keyRing.getPubKeyRing())
.map(accountAgeWitness -> {
try {
checkArgument(!accountAgeWitnessService.isFilteredWitness(accountAgeWitness), "Invalid account age witness");
long witnessSignDate = accountAgeWitnessService.getWitnessSignDate(accountAgeWitness);
long ageInDays = (System.currentTimeMillis() - witnessSignDate) / TimeUnit.DAYS.toMillis(1);
if (!DevEnv.isDevMode()) {
checkArgument(witnessSignDate > 0, "Account is not signed yet");
checkArgument(ageInDays > 60, "Account must have been signed at least 61 days ago");
}
String hashAsHex = Hex.encode(accountAgeWitness.getHash());
String message = profileId + hashAsHex + accountAgeWitness.getDate() + witnessSignDate;
KeyPair signatureKeyPair = keyRing.getSignatureKeyPair();
String signatureBase64 = Sig.sign(signatureKeyPair.getPrivate(), message);
String pubKeyBase64 = Base64.getEncoder().encodeToString(Sig.getPublicKeyBytes(signatureKeyPair.getPublic()));
SignedWitnessDto dto = new SignedWitnessDto(profileId,
hashAsHex,
accountAgeWitness.getDate(),
witnessSignDate,
pubKeyBase64,
signatureBase64);
return JsonUtil.objectToJson(dto);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
} }

View File

@ -1819,8 +1819,12 @@ If you do not understand these requirements, do not trade L-BTC on Bisq.
account.fiat.yourFiatAccounts=Your national currency accounts account.fiat.yourFiatAccounts=Your national currency accounts
account.fiat.exportAccountAge=Export account age for Bisq 2 account.fiat.exportAccountAge=Export account age for Bisq 2
account.fiat.exportAccountAge.popup=Your account age and Bisq 2 profile ID got signed and the data is copied \ account.fiat.exportAccountAge.popup=Your 'account age' and Bisq 2 profile ID got signed and the data is copied \
to the clipboard.\n\ to the clipboard.\n\n\This is the data in json format:\n\n\{0}\n\n\
Go back to Bisq 2 and follow the instructions there.
account.fiat.signedWitness=Export signed witness for Bisq 2
account.fiat.signedWitness.popup=Your 'signed account age witness' and Bisq 2 profile ID got signed and the data is copied \
to the clipboard.\n\n\This is the data in json format:\n\n\{0}\n\n\
Go back to Bisq 2 and follow the instructions there. Go back to Bisq 2 and follow the instructions there.
account.backup.title=Backup wallet account.backup.title=Backup wallet
account.backup.location=Backup location account.backup.location=Backup location

View File

@ -510,35 +510,65 @@ public class FiatAccountsView extends PaymentAccountsView<GridPane, FiatAccounts
GridPane.setRowSpan(accountTitledGroupBg, paymentMethodForm.getRowSpan()); GridPane.setRowSpan(accountTitledGroupBg, paymentMethodForm.getRowSpan());
Button exportAccountAgeButton = new AutoTooltipButton(Res.get("account.fiat.exportAccountAge")); Button exportAccountAgeButton = new AutoTooltipButton(Res.get("account.fiat.exportAccountAge"));
exportAccountAgeButton.setOnAction(event -> onExportAccountAge(paymentMethodForm.getPaymentAccount())); exportAccountAgeButton.setOnAction(event -> onExportAccountAgeForBisq2(paymentMethodForm.getPaymentAccount()));
exportAccountAgeButton.setMaxWidth(Double.MAX_VALUE); exportAccountAgeButton.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(exportAccountAgeButton, Priority.ALWAYS); HBox.setHgrow(exportAccountAgeButton, Priority.ALWAYS);
Button signedWitnessButton = new AutoTooltipButton(Res.get("account.fiat.signedWitness"));
signedWitnessButton.setOnAction(event -> onExportSignedWitnessForBisq2(paymentMethodForm.getPaymentAccount()));
signedWitnessButton.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(signedWitnessButton, Priority.ALWAYS);
HBox hBox = (HBox) cancelButton.getParent(); HBox hBox = (HBox) cancelButton.getParent();
hBox.getChildren().add(exportAccountAgeButton); hBox.getChildren().addAll(exportAccountAgeButton, signedWitnessButton);
model.onSelectAccount(current); model.onSelectAccount(current);
} }
} }
private void onExportAccountAge(PaymentAccount paymentAccount) { private void onExportAccountAgeForBisq2(PaymentAccount paymentAccount) {
String prefix = "BISQ2_ACCOUNT_AGE:";
try {
String profileId = getProfileIdFromClipBoard(prefix);
AccountAgeWitnessUtils.signAccountAgeAndBisq2ProfileId(accountAgeWitnessService, paymentAccount, keyRing, profileId)
.ifPresent(json -> {
Utilities.copyToClipboard(prefix + json);
new Popup().information(Res.get("account.fiat.exportAccountAge.popup", json))
.width(900).show();
});
} catch (Exception e) {
String error = e.getCause() != null ? e.getCause().getMessage() : e.getMessage();
new Popup().warning(error).show();
}
}
private void onExportSignedWitnessForBisq2(PaymentAccount paymentAccount) {
String prefix = "BISQ2_SIGNED_WITNESS:";
try {
String profileId = getProfileIdFromClipBoard(prefix);
AccountAgeWitnessUtils.signSignedWitnessAndBisq2ProfileId(accountAgeWitnessService, paymentAccount, keyRing, profileId)
.ifPresent(json -> {
Utilities.copyToClipboard(prefix + json);
new Popup().information(Res.get("account.fiat.signedWitness.popup", json))
.width(900).show();
});
} catch (Exception e) {
String error = e.getCause() != null ? e.getCause().getMessage() : e.getMessage();
new Popup().warning(error).show();
}
}
private String getProfileIdFromClipBoard(String prefix) {
String clipboardText = Clipboard.getSystemClipboard().getString(); String clipboardText = Clipboard.getSystemClipboard().getString();
String prefix = "BISQ2_ACCOUNT_AGE_REQUEST:";
if (clipboardText != null && clipboardText.startsWith(prefix)) { if (clipboardText != null && clipboardText.startsWith(prefix)) {
String profileId = clipboardText.replace(prefix, ""); String profileId = clipboardText.replace(prefix, "");
if (profileId.length() == 40) { if (profileId.length() == 40) {
try { Hex.decode(profileId);
Hex.decode(profileId); return profileId;
AccountAgeWitnessUtils.exportAccount(accountAgeWitnessService, paymentAccount, keyRing, profileId)
.ifPresent(json -> {
Utilities.copyToClipboard(json);
new Popup().information(Res.get("account.fiat.exportAccountAge.popup")).show();
});
return;
} catch (Throwable ignore) {
}
} }
} }
log.warn("Clipboard text not in expected format."); throw new RuntimeException("Clipboard text not in expected format. " + clipboardText);
} }