Only use accumulated hash in Witness object

This commit is contained in:
Manfred Karrer 2017-10-21 14:07:48 -05:00
parent a33923cc1e
commit e56fc44956
No known key found for this signature in database
GPG key ID: 401250966A6B2C46
43 changed files with 666 additions and 713 deletions

View file

@ -248,7 +248,7 @@ public class CurrencyUtil {
if (isCryptoCurrency(currencyCode) && cryptoCurrencyOptional.isPresent()) { if (isCryptoCurrency(currencyCode) && cryptoCurrencyOptional.isPresent()) {
return Optional.of(cryptoCurrencyOptional.get()); return Optional.of(cryptoCurrencyOptional.get());
} else { } else {
return Optional.empty(); return Optional.<TradeCurrency>empty();
} }
} }
} }

View file

@ -24,6 +24,7 @@ import com.google.gson.*;
import io.bisq.common.crypto.LimitedKeyStrengthException; import io.bisq.common.crypto.LimitedKeyStrengthException;
import javafx.scene.input.*; import javafx.scene.input.*;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.bitcoinj.core.Utils; import org.bitcoinj.core.Utils;
@ -379,6 +380,22 @@ public class Utilities {
return new KeyCodeCombination(keyCode, KeyCombination.ALT_DOWN).match(keyEvent); return new KeyCodeCombination(keyCode, KeyCombination.ALT_DOWN).match(keyEvent);
} }
public static byte[] concatenateByteArrays(byte[] array1, byte[] array2) {
return ArrayUtils.addAll(array1, array2);
}
public static byte[] concatenateByteArrays(byte[] array1, byte[] array2, byte[] array3) {
return ArrayUtils.addAll(array1, ArrayUtils.addAll(array2, array3));
}
public static byte[] concatenateByteArrays(byte[] array1, byte[] array2, byte[] array3, byte[] array4) {
return ArrayUtils.addAll(array1, ArrayUtils.addAll(array2, ArrayUtils.addAll(array3, array4)));
}
public static byte[] concatenateByteArrays(byte[] array1, byte[] array2, byte[] array3, byte[] array4, byte[] array5) {
return ArrayUtils.addAll(array1, ArrayUtils.addAll(array2, ArrayUtils.addAll(array3, ArrayUtils.addAll(array4, array5))));
}
private static class AnnotationExclusionStrategy implements ExclusionStrategy { private static class AnnotationExclusionStrategy implements ExclusionStrategy {
@Override @Override
public boolean shouldSkipField(FieldAttributes f) { public boolean shouldSkipField(FieldAttributes f) {

View file

@ -195,8 +195,9 @@ message PayDepositRequest {
NodeAddress arbitrator_node_address = 19; NodeAddress arbitrator_node_address = 19;
NodeAddress mediator_node_address = 20; NodeAddress mediator_node_address = 20;
string uid = 21; string uid = 21;
bytes account_age_witness_nonce = 22; bytes account_age_witness_signature_of_account_data = 22;
bytes account_age_witness_signature_of_nonce = 23; bytes account_age_witness_nonce = 23;
bytes account_age_witness_signature_of_nonce = 24;
} }
message PublishDepositTxRequest { message PublishDepositTxRequest {
@ -211,8 +212,9 @@ message PublishDepositTxRequest {
bytes maker_multi_sig_pub_key = 9; bytes maker_multi_sig_pub_key = 9;
NodeAddress sender_node_address = 10; NodeAddress sender_node_address = 10;
string uid = 11; string uid = 11;
bytes account_age_witness_nonce = 12; bytes account_age_witness_signature_of_account_data = 12;
bytes account_age_witness_signature_of_nonce = 13; bytes account_age_witness_nonce = 13;
bytes account_age_witness_signature_of_nonce = 14;
} }
message DepositTxPublishedMessage { message DepositTxPublishedMessage {
@ -661,9 +663,7 @@ message CompensationRequestPayload {
message AccountAgeWitness { message AccountAgeWitness {
bytes hash = 1; bytes hash = 1;
bytes sig_pub_key_hash = 2; int64 date = 2;
bytes signature = 3;
int64 date = 4;
} }
@ -1198,8 +1198,9 @@ message TradingPeer {
repeated RawTransactionInput raw_transaction_inputs = 9; repeated RawTransactionInput raw_transaction_inputs = 9;
int64 change_output_value = 10; int64 change_output_value = 10;
string change_output_address = 11; string change_output_address = 11;
bytes account_age_witness_nonce = 12; bytes account_age_witness_signature_of_account_data = 12;
bytes account_age_witness_signature_of_nonce = 13; bytes account_age_witness_nonce = 13;
bytes account_age_witness_signature_of_nonce = 14;
} }
message AccountAgeWitnessMap { message AccountAgeWitnessMap {

View file

@ -0,0 +1,35 @@
/*
* 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 io.bisq.common.util;
import org.junit.Test;
import java.util.Arrays;
import static org.junit.Assert.assertTrue;
public class UtilitiesTest {
@Test
public void testConcatenateByteArrays() {
assertTrue(Arrays.equals(new byte[]{0x01, 0x02}, Utilities.concatenateByteArrays(new byte[]{0x01}, new byte[]{0x02})));
assertTrue(Arrays.equals(new byte[]{0x01, 0x02, 0x03}, Utilities.concatenateByteArrays(new byte[]{0x01}, new byte[]{0x02}, new byte[]{0x03})));
assertTrue(Arrays.equals(new byte[]{0x01, 0x02, 0x03, 0x04}, Utilities.concatenateByteArrays(new byte[]{0x01}, new byte[]{0x02}, new byte[]{0x03}, new byte[]{0x04})));
assertTrue(Arrays.equals(new byte[]{0x01, 0x02, 0x03, 0x04, 0x05}, Utilities.concatenateByteArrays(new byte[]{0x01}, new byte[]{0x02}, new byte[]{0x03}, new byte[]{0x04}, new byte[]{0x05})));
}
}

View file

@ -787,7 +787,7 @@ public class BtcWalletService extends WalletService {
throw new AddressEntryException("No Addresses for withdraw found in our wallet"); throw new AddressEntryException("No Addresses for withdraw found in our wallet");
sendRequest.coinSelector = new BtcCoinSelector(walletsSetup.getAddressesFromAddressEntries(addressEntries)); sendRequest.coinSelector = new BtcCoinSelector(walletsSetup.getAddressesFromAddressEntries(addressEntries));
Optional<AddressEntry> addressEntryOptional = Optional.empty(); Optional<AddressEntry> addressEntryOptional = Optional.<AddressEntry>empty();
AddressEntry changeAddressAddressEntry = null; AddressEntry changeAddressAddressEntry = null;
if (changeAddress != null) if (changeAddress != null)
addressEntryOptional = findAddressEntry(changeAddress, AddressEntry.Context.AVAILABLE); addressEntryOptional = findAddressEntry(changeAddress, AddressEntry.Context.AVAILABLE);

View file

@ -302,11 +302,11 @@ public class Offer implements NetworkPayload, PersistablePayload {
} }
public Optional<String> getAccountAgeWitnessHash() { public Optional<String> getAccountAgeWitnessHashAsHex() {
if (getExtraDataMap() != null && getExtraDataMap().containsKey(OfferPayload.ACCOUNT_AGE_WITNESS_HASH)) if (getExtraDataMap() != null && getExtraDataMap().containsKey(OfferPayload.ACCOUNT_AGE_WITNESS_HASH))
return Optional.of(getExtraDataMap().get(OfferPayload.ACCOUNT_AGE_WITNESS_HASH)); return Optional.of(getExtraDataMap().get(OfferPayload.ACCOUNT_AGE_WITNESS_HASH));
else else
return Optional.empty(); return Optional.<String>empty();
} }
// domain properties // domain properties

View file

@ -67,7 +67,7 @@ public final class OfferPayload implements ProtectedStoragePayload, RequiresOwne
} }
// Keys for extra map // Keys for extra map
public static final String ACCOUNT_AGE_WITNESS_HASH = "accountAgeWitness"; public static final String ACCOUNT_AGE_WITNESS_HASH = "accountAgeWitnessHash";
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -31,31 +31,21 @@ import lombok.extern.slf4j.Slf4j;
import java.util.Date; import java.util.Date;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
// Object has about 94 raw bytes (about 101 bytes is size of PB object) // Object has about 28 raw bytes (29 bytes is size of PB object)
// With 100 000 entries we get 53.5 MB of data. Old entries will be shipped with the MapEntry resource file, // With 1 000 000 entries we get 29 MB of data. Old entries will be shipped with the MapEntry resource file,
// so only the newly added objects since the last release will not be loaded over the P2P network. // so only the newly added objects since the last release will be retrieved over the P2P network.
// TODO Get rid of sigPubKey and replace by hash of sigPubKey. That will reduce the data size to 118 bytes.
// Using EC signatures would produce longer signatures (71 bytes)
@Slf4j @Slf4j
@Value @Value
public class AccountAgeWitness implements LazyProcessedPayload, PersistableNetworkPayload, PersistableEnvelope, PublishDateVerifiedPayload { public class AccountAgeWitness implements LazyProcessedPayload, PersistableNetworkPayload, PersistableEnvelope, PublishDateVerifiedPayload {
private static final long TOLERANCE = TimeUnit.DAYS.toMillis(1); private static final long TOLERANCE = TimeUnit.DAYS.toMillis(1);
private final byte[] hash; // Ripemd160(Sha256(data)) hash 20 bytes private final byte[] hash; // Ripemd160(Sha256(concatenated accountHash, signature and sigPubKey)); 20 bytes
private final byte[] sigPubKeyHash; // Ripemd160(Sha256(sigPubKey)) hash 20 bytes
private final byte[] signature; // about 46 bytes
private final long date; // 8 byte private final long date; // 8 byte
public AccountAgeWitness(byte[] hash, public AccountAgeWitness(byte[] hash,
byte[] sigPubKeyHash,
byte[] signature,
long date) { long date) {
this.hash = hash; this.hash = hash;
this.sigPubKeyHash = sigPubKeyHash;
this.signature = signature;
this.date = date; this.date = date;
log.info("new AccountAgeWitness: hash={}, date={} ", Utilities.bytesAsHexString(hash), new Date(date));
} }
@ -68,8 +58,6 @@ public class AccountAgeWitness implements LazyProcessedPayload, PersistableNetwo
public PB.PersistableNetworkPayload toProtoMessage() { public PB.PersistableNetworkPayload toProtoMessage() {
final PB.AccountAgeWitness.Builder builder = PB.AccountAgeWitness.newBuilder() final PB.AccountAgeWitness.Builder builder = PB.AccountAgeWitness.newBuilder()
.setHash(ByteString.copyFrom(hash)) .setHash(ByteString.copyFrom(hash))
.setSigPubKeyHash(ByteString.copyFrom(sigPubKeyHash))
.setSignature(ByteString.copyFrom(signature))
.setDate(date); .setDate(date);
return PB.PersistableNetworkPayload.newBuilder().setAccountAgeWitness(builder).build(); return PB.PersistableNetworkPayload.newBuilder().setAccountAgeWitness(builder).build();
} }
@ -81,8 +69,6 @@ public class AccountAgeWitness implements LazyProcessedPayload, PersistableNetwo
public static AccountAgeWitness fromProto(PB.AccountAgeWitness proto) { public static AccountAgeWitness fromProto(PB.AccountAgeWitness proto) {
return new AccountAgeWitness( return new AccountAgeWitness(
proto.getHash().toByteArray(), proto.getHash().toByteArray(),
proto.getSigPubKeyHash().toByteArray(),
proto.getSignature().toByteArray(),
proto.getDate()); proto.getDate());
} }
@ -101,13 +87,6 @@ public class AccountAgeWitness implements LazyProcessedPayload, PersistableNetwo
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
//TODO impl. here or in caller?
// We allow max 1 day time difference
public boolean isDateValid() {
return new Date().getTime() - date < TimeUnit.DAYS.toMillis(1);
}
public P2PDataStorage.ByteArray getHashAsByteArray() { public P2PDataStorage.ByteArray getHashAsByteArray() {
return new P2PDataStorage.ByteArray(hash); return new P2PDataStorage.ByteArray(hash);
} }
@ -116,8 +95,6 @@ public class AccountAgeWitness implements LazyProcessedPayload, PersistableNetwo
public String toString() { public String toString() {
return "AccountAgeWitness{" + return "AccountAgeWitness{" +
"\n hash=" + Utilities.bytesAsHexString(hash) + "\n hash=" + Utilities.bytesAsHexString(hash) +
",\n sigPubKeyHash=" + Utilities.bytesAsHexString(sigPubKeyHash) +
",\n signature=" + Utilities.bytesAsHexString(signature) +
",\n date=" + new Date(date) + ",\n date=" + new Date(date) +
"\n}"; "\n}";
} }

View file

@ -18,8 +18,8 @@
package io.bisq.core.payment; package io.bisq.core.payment;
import io.bisq.common.crypto.CryptoException; import io.bisq.common.crypto.CryptoException;
import io.bisq.common.crypto.Hash;
import io.bisq.common.crypto.KeyRing; import io.bisq.common.crypto.KeyRing;
import io.bisq.common.crypto.PubKeyRing;
import io.bisq.common.crypto.Sig; import io.bisq.common.crypto.Sig;
import io.bisq.common.handlers.ErrorMessageHandler; import io.bisq.common.handlers.ErrorMessageHandler;
import io.bisq.common.locale.CurrencyUtil; import io.bisq.common.locale.CurrencyUtil;
@ -31,7 +31,6 @@ import io.bisq.core.payment.payload.PaymentMethod;
import io.bisq.network.p2p.P2PService; import io.bisq.network.p2p.P2PService;
import io.bisq.network.p2p.storage.P2PDataStorage; import io.bisq.network.p2p.storage.P2PDataStorage;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import javax.inject.Inject; import javax.inject.Inject;
@ -52,12 +51,23 @@ public class AccountAgeWitnessService {
private final P2PService p2PService; private final P2PService p2PService;
private final Map<P2PDataStorage.ByteArray, AccountAgeWitness> accountAgeWitnessMap = new HashMap<>(); private final Map<P2PDataStorage.ByteArray, AccountAgeWitness> accountAgeWitnessMap = new HashMap<>();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
public AccountAgeWitnessService(KeyRing keyRing, P2PService p2PService) { public AccountAgeWitnessService(KeyRing keyRing, P2PService p2PService) {
this.keyRing = keyRing; this.keyRing = keyRing;
this.p2PService = p2PService; this.p2PService = p2PService;
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
public void onAllServicesInitialized() { public void onAllServicesInitialized() {
p2PService.getP2PDataStorage().addPersistableNetworkPayloadMapListener(payload -> { p2PService.getP2PDataStorage().addPersistableNetworkPayloadMapListener(payload -> {
if (payload instanceof AccountAgeWitness) if (payload instanceof AccountAgeWitness)
@ -71,49 +81,29 @@ public class AccountAgeWitnessService {
}); });
} }
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
private void addToMap(AccountAgeWitness accountAgeWitness) { private void addToMap(AccountAgeWitness accountAgeWitness) {
log.debug("addToMap hash=" + Utilities.bytesAsHexString(accountAgeWitness.getHash())); log.debug("addToMap hash=" + Utilities.bytesAsHexString(accountAgeWitness.getHash()));
if (!accountAgeWitnessMap.containsKey(accountAgeWitness.getHashAsByteArray())) if (!accountAgeWitnessMap.containsKey(accountAgeWitness.getHashAsByteArray()))
accountAgeWitnessMap.put(accountAgeWitness.getHashAsByteArray(), accountAgeWitness); accountAgeWitnessMap.put(accountAgeWitness.getHashAsByteArray(), accountAgeWitness);
} }
public void publishAccountAgeWitness(PaymentAccountPayload paymentAccountPayload) { public void publishMyAccountAgeWitness(PaymentAccountPayload paymentAccountPayload) {
try { AccountAgeWitness accountAgeWitness = getMyWitness(paymentAccountPayload);
AccountAgeWitness accountAgeWitness = getAccountAgeWitness(paymentAccountPayload);
if (!accountAgeWitnessMap.containsKey(accountAgeWitness.getHashAsByteArray())) if (!accountAgeWitnessMap.containsKey(accountAgeWitness.getHashAsByteArray()))
p2PService.addPersistableNetworkPayload(accountAgeWitness); p2PService.addPersistableNetworkPayload(accountAgeWitness);
} catch (CryptoException e) {
e.printStackTrace();
log.error(e.toString());
}
} }
public Optional<AccountAgeWitness> getWitnessByHash(String hashAsHex) { ///////////////////////////////////////////////////////////////////////////////////////////
P2PDataStorage.ByteArray hashAsByteArray = new P2PDataStorage.ByteArray(Utilities.decodeFromHex(hashAsHex)); // Generic
return accountAgeWitnessMap.containsKey(hashAsByteArray) ? Optional.of(accountAgeWitnessMap.get(hashAsByteArray)) : Optional.<AccountAgeWitness>empty(); ///////////////////////////////////////////////////////////////////////////////////////////
}
public Optional<AccountAgeWitness> getWitnessByPaymentAccountPayload(PaymentAccountPayload paymentAccountPayload) { public byte[] getAccountInputDataWithSalt(PaymentAccountPayload paymentAccountPayload) {
return getWitnessByHash(getWitnessHashAsHex(paymentAccountPayload)); return Utilities.concatenateByteArrays(paymentAccountPayload.getAgeWitnessInputData(), paymentAccountPayload.getSalt());
}
public long getAccountAge(Offer offer) {
if (offer.getAccountAgeWitnessHash().isPresent())
return getAccountAge(getWitnessByHash(offer.getAccountAgeWitnessHash().get()));
else
return 0L;
}
public long getAccountAge(PaymentAccountPayload paymentAccountPayload) {
return getAccountAge(getWitnessByPaymentAccountPayload(paymentAccountPayload));
}
private long getAccountAge(Optional<AccountAgeWitness> accountAgeWitnessOptional) {
if (accountAgeWitnessOptional.isPresent()) {
return new Date().getTime() - accountAgeWitnessOptional.get().getDate();
} else {
return 0L;
}
} }
public long getAccountAge(AccountAgeWitness accountAgeWitness) { public long getAccountAge(AccountAgeWitness accountAgeWitness) {
@ -130,59 +120,11 @@ public class AccountAgeWitnessService {
} }
} }
private AccountAgeWitness getAccountAgeWitness(PaymentAccountPayload paymentAccountPayload) throws CryptoException { private long getTradeLimit(PaymentMethod paymentMethod, String currencyCode, Optional<AccountAgeWitness> accountAgeWitnessOptional) {
byte[] hash = getWitnessHash(paymentAccountPayload); final long maxTradeLimit = paymentMethod.getMaxTradeLimitAsCoin(currencyCode).value;
byte[] signature = Sig.sign(keyRing.getSignatureKeyPair().getPrivate(), hash);
byte[] sigPubKeyHash = Hash.getSha256Ripemd160hash(keyRing.getPubKeyRing().getSignaturePubKeyBytes());
long now = new Date().getTime();
//TODO
// test
//now -= TimeUnit.DAYS.toMillis(75);
return new AccountAgeWitness(hash,
sigPubKeyHash,
signature,
now);
}
public byte[] getWitnessHash(PaymentAccountPayload paymentAccountPayload) {
return getWitnessHash(paymentAccountPayload, paymentAccountPayload.getSalt());
}
public String getWitnessHashAsHex(PaymentAccountPayload paymentAccountPayload) {
return Utilities.bytesAsHexString(getWitnessHash(paymentAccountPayload));
}
private byte[] getWitnessHash(PaymentAccountPayload paymentAccountPayload, byte[] salt) {
byte[] ageWitnessInputData = paymentAccountPayload.getAgeWitnessInputData();
final byte[] combined = ArrayUtils.addAll(ageWitnessInputData, salt);
final byte[] hash = Hash.getSha256Ripemd160hash(combined);
log.debug("getWitnessHash paymentAccountPayload={}, salt={}, ageWitnessInputData={}, combined={}, hash={}",
paymentAccountPayload.getPaymentDetails(),
Utilities.encodeToHex(salt),
Utilities.encodeToHex(ageWitnessInputData),
Utilities.encodeToHex(combined),
Utilities.encodeToHex(hash));
return hash;
}
public long getTradeLimit(PaymentAccount paymentAccount, String currencyCode) {
return getTradeLimit(paymentAccount.getPaymentAccountPayload(), currencyCode);
}
public long getTradeLimit(PaymentAccountPayload paymentAccountPayload, String currencyCode) {
final long maxTradeLimit = PaymentMethod.getPaymentMethodById(paymentAccountPayload.getPaymentMethodId()).getMaxTradeLimitAsCoin(currencyCode).value;
if (CurrencyUtil.isFiatCurrency(currencyCode)) { if (CurrencyUtil.isFiatCurrency(currencyCode)) {
double factor; double factor;
// TODO test
/*Optional<AccountAgeWitness> accountAgeWitnessOptional = paymentAccount.getName() != null ?
getWitnessByHash(getWitnessHashAsHex(paymentAccountPayload)) :
Optional.empty();*/
Optional<AccountAgeWitness> accountAgeWitnessOptional = getWitnessByHash(getWitnessHashAsHex(paymentAccountPayload));
AccountAge accountAgeCategory = accountAgeWitnessOptional.isPresent() ? AccountAge accountAgeCategory = accountAgeWitnessOptional.isPresent() ?
getAccountAgeCategory(getAccountAge((accountAgeWitnessOptional.get()))) : getAccountAgeCategory(getAccountAge((accountAgeWitnessOptional.get()))) :
AccountAgeWitnessService.AccountAge.LESS_ONE_MONTH; AccountAgeWitnessService.AccountAge.LESS_ONE_MONTH;
@ -236,38 +178,113 @@ public class AccountAgeWitnessService {
} }
} }
public boolean verifyAccountAgeWitness(byte[] peersAgeWitnessInputData,
///////////////////////////////////////////////////////////////////////////////////////////
// My witness
///////////////////////////////////////////////////////////////////////////////////////////
public AccountAgeWitness getMyWitness(PaymentAccountPayload paymentAccountPayload) {
try {
byte[] accountInputDataWithSalt = getAccountInputDataWithSalt(paymentAccountPayload);
byte[] hash = Utilities.concatenateByteArrays(accountInputDataWithSalt,
Sig.sign(keyRing.getSignatureKeyPair().getPrivate(), accountInputDataWithSalt),
keyRing.getPubKeyRing().getSignaturePubKeyBytes());
long date = new Date().getTime();
//TODO
// test
//date -= TimeUnit.DAYS.toMillis(75);
return new AccountAgeWitness(hash, date);
} catch (CryptoException e) {
log.error(e.toString());
e.printStackTrace();
throw new RuntimeException(e);
}
}
public byte[] getMyWitnessHash(PaymentAccountPayload paymentAccountPayload) {
return getMyWitness(paymentAccountPayload).getHash();
}
public String getMyWitnessHashAsHex(PaymentAccountPayload paymentAccountPayload) {
return Utilities.bytesAsHexString(getMyWitnessHash(paymentAccountPayload));
}
public long getMyAccountAge(PaymentAccountPayload paymentAccountPayload) {
return getAccountAge(getMyWitness(paymentAccountPayload));
}
public long getMyTradeLimit(PaymentAccount paymentAccount, String currencyCode) {
return getTradeLimit(paymentAccount.getPaymentMethod(), currencyCode, Optional.of(getMyWitness(paymentAccount.getPaymentAccountPayload())));
}
///////////////////////////////////////////////////////////////////////////////////////////
// Peers witness
///////////////////////////////////////////////////////////////////////////////////////////
public Optional<AccountAgeWitness> getPeersWitnessByHash(byte[] hash) {
P2PDataStorage.ByteArray hashAsByteArray = new P2PDataStorage.ByteArray(hash);
return accountAgeWitnessMap.containsKey(hashAsByteArray) ? Optional.of(accountAgeWitnessMap.get(hashAsByteArray)) : Optional.<AccountAgeWitness>empty();
}
public Optional<AccountAgeWitness> getPeersWitnessByHashAsHex(String hashAsHex) {
return getPeersWitnessByHash(Utilities.decodeFromHex(hashAsHex));
}
public long getPeersAccountAge(Offer offer) {
final Optional<String> accountAgeWitnessHash = offer.getAccountAgeWitnessHashAsHex();
final Optional<AccountAgeWitness> witnessByHashAsHex = accountAgeWitnessHash.isPresent() ?
getPeersWitnessByHashAsHex(accountAgeWitnessHash.get()) :
Optional.<AccountAgeWitness>empty();
return witnessByHashAsHex.isPresent() ? getAccountAge(witnessByHashAsHex.get()) : 0L;
}
public long getPeersTradeLimit(PaymentAccountPayload paymentAccountPayload, String currencyCode, Optional<AccountAgeWitness> accountAgeWitnessOptional) {
return getTradeLimit(PaymentMethod.getPaymentMethodById(paymentAccountPayload.getPaymentMethodId()), currencyCode, accountAgeWitnessOptional);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Verification
///////////////////////////////////////////////////////////////////////////////////////////
public boolean verifyPeersAccountAgeWitness(Offer offer,
PaymentAccountPayload peersPaymentAccountPayload,
AccountAgeWitness witness, AccountAgeWitness witness,
byte[] peersSalt, PubKeyRing peersPubKeyRing,
PublicKey peersPublicKey, byte[] peersSignatureOfAccountHash,
byte[] nonce, byte[] nonce,
byte[] signatureOfNonce, byte[] signatureOfNonce,
ErrorMessageHandler errorMessageHandler) { ErrorMessageHandler errorMessageHandler) {
// Check if trade date in witness is not older than the release date of that feature (was added in v0.6) // Check if trade date in witness is not older than the release date of that feature (was added in v0.6)
// TODO set date before releasing // TODO set date before releasing
if (!isTradeDateAfterReleaseDate(witness.getDate(), new GregorianCalendar(2017, GregorianCalendar.OCTOBER, 17).getTime(), errorMessageHandler)) if (!isTradeDateAfterReleaseDate(witness.getDate(), new GregorianCalendar(2017, GregorianCalendar.OCTOBER, 17).getTime(), errorMessageHandler))
return false; return false;
// Check if peer's pubkey is matching the one from the witness data final byte[] peersAccountInputDataWithSalt = Utilities.concatenateByteArrays(peersPaymentAccountPayload.getAgeWitnessInputData(), peersPaymentAccountPayload.getSalt());
if (!verifySigPubKeyHash(witness.getSigPubKeyHash(), peersPublicKey, errorMessageHandler)) byte[] hash = Utilities.concatenateByteArrays(peersAccountInputDataWithSalt, peersSignatureOfAccountHash, peersPubKeyRing.getSignaturePubKeyBytes());
return false;
final byte[] combined = ArrayUtils.addAll(peersAgeWitnessInputData, peersSalt); // Check if the hash in the witness data matches the hash derived from the data provided by the peer
byte[] hash = Hash.getSha256Ripemd160hash(combined);
// Check if the hash in the witness data matches the peer's payment account input data + salt
if (!verifyWitnessHash(witness.getHash(), hash, errorMessageHandler)) if (!verifyWitnessHash(witness.getHash(), hash, errorMessageHandler))
return false; return false;
// Check if the witness signature is correct // Check if the witness signature is correct
if (!verifySignature(peersPublicKey, hash, witness.getSignature(), errorMessageHandler)) if (!verifyPeersTradeLimit(offer, peersPaymentAccountPayload, errorMessageHandler))
return false;
// Check if the witness signature is correct
if (!verifySignature(peersPubKeyRing.getSignaturePubKey(), peersAccountInputDataWithSalt, peersSignatureOfAccountHash, errorMessageHandler))
return false; return false;
// Check if the signature of the nonce is correct // Check if the signature of the nonce is correct
return verifySignatureOfNonce(peersPublicKey, nonce, signatureOfNonce, errorMessageHandler); return verifySignatureOfNonce(peersPubKeyRing.getSignaturePubKey(), nonce, signatureOfNonce, errorMessageHandler);
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Package scope verification subroutines
///////////////////////////////////////////////////////////////////////////////////////////
boolean isTradeDateAfterReleaseDate(long witnessDateAsLong, Date ageWitnessReleaseDate, ErrorMessageHandler errorMessageHandler) { boolean isTradeDateAfterReleaseDate(long witnessDateAsLong, Date ageWitnessReleaseDate, ErrorMessageHandler errorMessageHandler) {
// Release date minus 1 day as tolerance for not synced clocks // Release date minus 1 day as tolerance for not synced clocks
Date releaseDateWithTolerance = new Date(ageWitnessReleaseDate.getTime() - TimeUnit.DAYS.toMillis(1)); Date releaseDateWithTolerance = new Date(ageWitnessReleaseDate.getTime() - TimeUnit.DAYS.toMillis(1));
@ -282,22 +299,7 @@ public class AccountAgeWitnessService {
return result; return result;
} }
boolean verifySigPubKeyHash(byte[] sigPubKeyHash, boolean verifyWitnessHash(byte[] witnessHash,
PublicKey peersPublicKey,
ErrorMessageHandler errorMessageHandler) {
final byte[] peersPublicKeyHash = Hash.getSha256Ripemd160hash(Sig.getPublicKeyBytes(peersPublicKey));
final boolean result = Arrays.equals(peersPublicKeyHash, sigPubKeyHash);
if (!result) {
final String msg = "sigPubKeyHash is not matching peers peersPublicKey. " +
"sigPubKeyHash=" + Utilities.bytesAsHexString(sigPubKeyHash) + ", peersPublicKeyHash=" +
Utilities.bytesAsHexString(peersPublicKeyHash);
log.warn(msg);
errorMessageHandler.handleErrorMessage(msg);
}
return result;
}
private boolean verifyWitnessHash(byte[] witnessHash,
byte[] hash, byte[] hash,
ErrorMessageHandler errorMessageHandler) { ErrorMessageHandler errorMessageHandler) {
final boolean result = Arrays.equals(witnessHash, hash); final boolean result = Arrays.equals(witnessHash, hash);
@ -310,6 +312,24 @@ public class AccountAgeWitnessService {
return result; return result;
} }
private boolean verifyPeersTradeLimit(Offer offer,
PaymentAccountPayload paymentAccountPayload,
ErrorMessageHandler errorMessageHandler) {
final Optional<String> offerHashAsHexOptional = offer.getAccountAgeWitnessHashAsHex();
Optional<AccountAgeWitness> accountAgeWitnessOptional = offerHashAsHexOptional.isPresent() ? getPeersWitnessByHashAsHex(offerHashAsHexOptional.get()) : Optional.<AccountAgeWitness>empty();
long maxTradeLimit = getPeersTradeLimit(paymentAccountPayload, offer.getCurrencyCode(), accountAgeWitnessOptional);
final Coin offerMaxTradeLimit = offer.getMaxTradeLimit();
boolean result = offerMaxTradeLimit.value == maxTradeLimit;
if (!result) {
String msg = "Offers max trade limit does not match with the one based on his account age.\n" +
"OfferMaxTradeLimit=" + offerMaxTradeLimit.toFriendlyString() +
"; Account age based MaxTradeLimit=" + Coin.valueOf(maxTradeLimit).toFriendlyString();
log.warn(msg);
errorMessageHandler.handleErrorMessage(msg);
}
return result;
}
boolean verifySignature(PublicKey peersPublicKey, boolean verifySignature(PublicKey peersPublicKey,
byte[] data, byte[] data,
byte[] signature, byte[] signature,
@ -351,34 +371,4 @@ public class AccountAgeWitnessService {
} }
return result; return result;
} }
public boolean verifyOffersAccountAgeWitness(PaymentAccountPayload paymentAccountPayload,
byte[] offersWitness,
ErrorMessageHandler errorMessageHandler) {
byte[] witnessHash = getWitnessHash(paymentAccountPayload, paymentAccountPayload.getSalt());
final boolean result = Arrays.equals(witnessHash, offersWitness);
if (!result) {
final String msg = "witnessHash is not matching peers offersWitness. " +
"witnessHash=" + Utilities.bytesAsHexString(witnessHash) + ", offersWitness=" + Utilities.bytesAsHexString(offersWitness);
log.warn(msg);
errorMessageHandler.handleErrorMessage(msg);
}
return result;
}
public boolean verifyTradeLimit(Offer offer,
PaymentAccountPayload paymentAccountPayload,
ErrorMessageHandler errorMessageHandler) {
long maxTradeLimit = getTradeLimit(paymentAccountPayload, offer.getCurrencyCode());
final Coin offerMaxTradeLimit = offer.getMaxTradeLimit();
final boolean result = offerMaxTradeLimit.value == maxTradeLimit;
if (!result) {
String msg = "Offers max trade limit does not match with the one we calculated.\n" +
"OfferMaxTradeLimit=" + offerMaxTradeLimit.toFriendlyString() +
"; MaxTradeLimit=" + Coin.valueOf(maxTradeLimit).toFriendlyString();
log.warn(msg);
errorMessageHandler.handleErrorMessage(msg);
}
return result;
}
} }

View file

@ -1,7 +1,6 @@
package io.bisq.core.payment; package io.bisq.core.payment;
import io.bisq.common.locale.TradeCurrency; import io.bisq.common.locale.TradeCurrency;
import io.bisq.common.util.Utilities;
import io.bisq.core.offer.Offer; import io.bisq.core.offer.Offer;
import io.bisq.core.payment.payload.PaymentMethod; import io.bisq.core.payment.payload.PaymentMethod;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
@ -9,7 +8,6 @@ import javafx.collections.ObservableList;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -105,30 +103,14 @@ public class PaymentAccountUtil {
public static Optional<PaymentAccount> getMostMaturePaymentAccountForOffer(Offer offer, public static Optional<PaymentAccount> getMostMaturePaymentAccountForOffer(Offer offer,
Set<PaymentAccount> paymentAccounts, Set<PaymentAccount> paymentAccounts,
AccountAgeWitnessService accountAgeWitnessService) { AccountAgeWitnessService service) {
List<PaymentAccount> list = paymentAccounts.stream() List<PaymentAccount> list = paymentAccounts.stream()
.filter(paymentAccount -> isPaymentAccountValidForOffer(offer, paymentAccount)) .filter(paymentAccount -> isPaymentAccountValidForOffer(offer, paymentAccount))
.sorted((o1, o2) -> { .sorted((o1, o2) -> {
final Optional<AccountAgeWitness> witness1 = accountAgeWitnessService.getWitnessByPaymentAccountPayload(o1.getPaymentAccountPayload()); return new Long(service.getAccountAge(service.getMyWitness(o2.getPaymentAccountPayload())))
log.debug("witness1 isPresent={}", witness1.isPresent()); .compareTo(service.getAccountAge(service.getMyWitness(o1.getPaymentAccountPayload())));
if (witness1.isPresent())
log.debug("witness1 HashAsHex={}, date={}", Utilities.bytesAsHexString(witness1.get().getHash()), new Date(witness1.get().getDate()));
long age1 = witness1.isPresent() ? accountAgeWitnessService.getAccountAge(witness1.get()) : 0;
final Optional<AccountAgeWitness> witness2 = accountAgeWitnessService.getWitnessByPaymentAccountPayload(o2.getPaymentAccountPayload());
log.debug("witness2 isPresent={}", witness2.isPresent());
if (witness2.isPresent())
log.debug("witness2 HashAsHex={}, date={}", Utilities.bytesAsHexString(witness2.get().getHash()), new Date(witness2.get().getDate()));
long age2 = witness2.isPresent() ? accountAgeWitnessService.getAccountAge(witness2.get()) : 0;
log.debug("AccountName 1 " + o1.getAccountName());
log.debug("AccountName 2 " + o2.getAccountName());
log.debug("age1 " + age1 / TimeUnit.DAYS.toMillis(1));
log.debug("age2 " + age2 / TimeUnit.DAYS.toMillis(1));
log.debug("result " + (new Long(age1).compareTo(age2)));
log.debug(" ");
return new Long(age2).compareTo(age1);
}).collect(Collectors.toList()); }).collect(Collectors.toList());
list.stream().forEach(e -> log.error("getMostMaturePaymentAccountForOffer AccountName={}, witnessHashAsHex={}", e.getAccountName(), accountAgeWitnessService.getWitnessHashAsHex(e.getPaymentAccountPayload()))); list.stream().forEach(e -> log.error("getMostMaturePaymentAccountForOffer AccountName={}, witnessHashAsHex={}", e.getAccountName(), service.getMyWitnessHashAsHex(e.getPaymentAccountPayload())));
final Optional<PaymentAccount> first = list.stream().findFirst(); final Optional<PaymentAccount> first = list.stream().findFirst();
if (first.isPresent()) if (first.isPresent())
log.debug("first={}", first.get().getAccountName()); log.debug("first={}", first.get().getAccountName());

View file

@ -62,6 +62,8 @@ public final class PayDepositRequest extends TradeMessage {
// added in v 0.6. can be null if we trade with an older peer // added in v 0.6. can be null if we trade with an older peer
@Nullable @Nullable
private final byte[] accountAgeWitnessSignatureOfAccountData;
@Nullable
private final byte[] accountAgeWitnessNonce; private final byte[] accountAgeWitnessNonce;
@Nullable @Nullable
private final byte[] accountAgeWitnessSignatureOfNonce; private final byte[] accountAgeWitnessSignatureOfNonce;
@ -88,6 +90,7 @@ public final class PayDepositRequest extends TradeMessage {
NodeAddress mediatorNodeAddress, NodeAddress mediatorNodeAddress,
String uid, String uid,
int messageVersion, int messageVersion,
@Nullable byte[] accountAgeWitnessSignatureOfAccountData,
@Nullable byte[] accountAgeWitnessNonce, @Nullable byte[] accountAgeWitnessNonce,
@Nullable byte[] accountAgeWitnessSignatureOfNonce) { @Nullable byte[] accountAgeWitnessSignatureOfNonce) {
super(messageVersion, tradeId); super(messageVersion, tradeId);
@ -111,6 +114,7 @@ public final class PayDepositRequest extends TradeMessage {
this.arbitratorNodeAddress = arbitratorNodeAddress; this.arbitratorNodeAddress = arbitratorNodeAddress;
this.mediatorNodeAddress = mediatorNodeAddress; this.mediatorNodeAddress = mediatorNodeAddress;
this.uid = uid; this.uid = uid;
this.accountAgeWitnessSignatureOfAccountData = accountAgeWitnessSignatureOfAccountData;
this.accountAgeWitnessNonce = accountAgeWitnessNonce; this.accountAgeWitnessNonce = accountAgeWitnessNonce;
this.accountAgeWitnessSignatureOfNonce = accountAgeWitnessSignatureOfNonce; this.accountAgeWitnessSignatureOfNonce = accountAgeWitnessSignatureOfNonce;
} }
@ -148,8 +152,9 @@ public final class PayDepositRequest extends TradeMessage {
.setUid(uid); .setUid(uid);
Optional.ofNullable(changeOutputAddress).ifPresent(builder::setChangeOutputAddress); Optional.ofNullable(changeOutputAddress).ifPresent(builder::setChangeOutputAddress);
Optional.ofNullable(accountAgeWitnessNonce).ifPresent(accountAgeWitnessNonce -> builder.setAccountAgeWitnessNonce(ByteString.copyFrom(accountAgeWitnessNonce))); Optional.ofNullable(accountAgeWitnessSignatureOfAccountData).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfAccountData(ByteString.copyFrom(e)));
Optional.ofNullable(accountAgeWitnessSignatureOfNonce).ifPresent(accountAgeWitnessSignatureOfNonce -> builder.setAccountAgeWitnessSignatureOfNonce(ByteString.copyFrom(accountAgeWitnessSignatureOfNonce))); Optional.ofNullable(accountAgeWitnessNonce).ifPresent(e -> builder.setAccountAgeWitnessNonce(ByteString.copyFrom(e)));
Optional.ofNullable(accountAgeWitnessSignatureOfNonce).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfNonce(ByteString.copyFrom(e)));
return getNetworkEnvelopeBuilder().setPayDepositRequest(builder).build(); return getNetworkEnvelopeBuilder().setPayDepositRequest(builder).build();
} }
@ -186,8 +191,9 @@ public final class PayDepositRequest extends TradeMessage {
NodeAddress.fromProto(proto.getMediatorNodeAddress()), NodeAddress.fromProto(proto.getMediatorNodeAddress()),
proto.getUid(), proto.getUid(),
messageVersion, messageVersion,
proto.getAccountAgeWitnessNonce().isEmpty() ? null : proto.getAccountAgeWitnessNonce().toByteArray(), ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfAccountData()),
proto.getAccountAgeWitnessSignatureOfNonce().isEmpty() ? null : proto.getAccountAgeWitnessSignatureOfNonce().toByteArray()); ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessNonce()),
ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfNonce()));
} }
@Override @Override
@ -213,6 +219,7 @@ public final class PayDepositRequest extends TradeMessage {
",\n arbitratorNodeAddress=" + arbitratorNodeAddress + ",\n arbitratorNodeAddress=" + arbitratorNodeAddress +
",\n mediatorNodeAddress=" + mediatorNodeAddress + ",\n mediatorNodeAddress=" + mediatorNodeAddress +
",\n uid='" + uid + '\'' + ",\n uid='" + uid + '\'' +
",\n accountAgeWitnessSignatureOfAccountData=" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfAccountData) +
",\n accountAgeWitnessNonce=" + Utilities.bytesAsHexString(accountAgeWitnessNonce) + ",\n accountAgeWitnessNonce=" + Utilities.bytesAsHexString(accountAgeWitnessNonce) +
",\n accountAgeWitnessSignatureOfNonce=" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfNonce) + ",\n accountAgeWitnessSignatureOfNonce=" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfNonce) +
"\n} " + super.toString(); "\n} " + super.toString();

View file

@ -19,6 +19,7 @@ package io.bisq.core.trade.messages;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import io.bisq.common.app.Version; import io.bisq.common.app.Version;
import io.bisq.common.proto.ProtoUtil;
import io.bisq.common.util.Utilities; import io.bisq.common.util.Utilities;
import io.bisq.core.btc.data.RawTransactionInput; import io.bisq.core.btc.data.RawTransactionInput;
import io.bisq.core.payment.payload.PaymentAccountPayload; import io.bisq.core.payment.payload.PaymentAccountPayload;
@ -54,6 +55,8 @@ public final class PublishDepositTxRequest extends TradeMessage implements Mailb
// added in v 0.6. can be null if we trade with an older peer // added in v 0.6. can be null if we trade with an older peer
@Nullable @Nullable
private final byte[] accountAgeWitnessSignatureOfAccountData;
@Nullable
private final byte[] accountAgeWitnessNonce; private final byte[] accountAgeWitnessNonce;
@Nullable @Nullable
private final byte[] accountAgeWitnessSignatureOfNonce; private final byte[] accountAgeWitnessSignatureOfNonce;
@ -69,6 +72,7 @@ public final class PublishDepositTxRequest extends TradeMessage implements Mailb
List<RawTransactionInput> makerInputs, List<RawTransactionInput> makerInputs,
NodeAddress senderNodeAddress, NodeAddress senderNodeAddress,
String uid, String uid,
@Nullable byte[] accountAgeWitnessSignatureOfAccountData,
@Nullable byte[] accountAgeWitnessNonce, @Nullable byte[] accountAgeWitnessNonce,
@Nullable byte[] accountAgeWitnessSignatureOfNonce) { @Nullable byte[] accountAgeWitnessSignatureOfNonce) {
this(tradeId, this(tradeId,
@ -83,6 +87,7 @@ public final class PublishDepositTxRequest extends TradeMessage implements Mailb
senderNodeAddress, senderNodeAddress,
uid, uid,
Version.getP2PMessageVersion(), Version.getP2PMessageVersion(),
accountAgeWitnessSignatureOfAccountData,
accountAgeWitnessNonce, accountAgeWitnessNonce,
accountAgeWitnessSignatureOfNonce); accountAgeWitnessSignatureOfNonce);
} }
@ -104,6 +109,7 @@ public final class PublishDepositTxRequest extends TradeMessage implements Mailb
NodeAddress senderNodeAddress, NodeAddress senderNodeAddress,
String uid, String uid,
int messageVersion, int messageVersion,
@Nullable byte[] accountAgeWitnessSignatureOfAccountData,
@Nullable byte[] accountAgeWitnessNonce, @Nullable byte[] accountAgeWitnessNonce,
@Nullable byte[] accountAgeWitnessSignatureOfNonce) { @Nullable byte[] accountAgeWitnessSignatureOfNonce) {
super(messageVersion, tradeId); super(messageVersion, tradeId);
@ -117,6 +123,7 @@ public final class PublishDepositTxRequest extends TradeMessage implements Mailb
this.makerInputs = makerInputs; this.makerInputs = makerInputs;
this.senderNodeAddress = senderNodeAddress; this.senderNodeAddress = senderNodeAddress;
this.uid = uid; this.uid = uid;
this.accountAgeWitnessSignatureOfAccountData = accountAgeWitnessSignatureOfAccountData;
this.accountAgeWitnessNonce = accountAgeWitnessNonce; this.accountAgeWitnessNonce = accountAgeWitnessNonce;
this.accountAgeWitnessSignatureOfNonce = accountAgeWitnessSignatureOfNonce; this.accountAgeWitnessSignatureOfNonce = accountAgeWitnessSignatureOfNonce;
} }
@ -136,8 +143,9 @@ public final class PublishDepositTxRequest extends TradeMessage implements Mailb
.setSenderNodeAddress(senderNodeAddress.toProtoMessage()) .setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setUid(uid); .setUid(uid);
Optional.ofNullable(accountAgeWitnessNonce).ifPresent(accountAgeWitnessNonce ->builder.setAccountAgeWitnessNonce(ByteString.copyFrom(accountAgeWitnessNonce))); Optional.ofNullable(accountAgeWitnessSignatureOfAccountData).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfAccountData(ByteString.copyFrom(e)));
Optional.ofNullable(accountAgeWitnessSignatureOfNonce).ifPresent(accountAgeWitnessSignatureOfNonce ->builder.setAccountAgeWitnessSignatureOfNonce(ByteString.copyFrom(accountAgeWitnessSignatureOfNonce))); Optional.ofNullable(accountAgeWitnessNonce).ifPresent(e -> builder.setAccountAgeWitnessNonce(ByteString.copyFrom(e)));
Optional.ofNullable(accountAgeWitnessSignatureOfNonce).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfNonce(ByteString.copyFrom(e)));
return getNetworkEnvelopeBuilder() return getNetworkEnvelopeBuilder()
.setPublishDepositTxRequest(builder) .setPublishDepositTxRequest(builder)
@ -161,8 +169,9 @@ public final class PublishDepositTxRequest extends TradeMessage implements Mailb
NodeAddress.fromProto(proto.getSenderNodeAddress()), NodeAddress.fromProto(proto.getSenderNodeAddress()),
proto.getUid(), proto.getUid(),
messageVersion, messageVersion,
proto.getAccountAgeWitnessNonce().isEmpty() ? null : proto.getAccountAgeWitnessNonce().toByteArray(), ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfAccountData()),
proto.getAccountAgeWitnessSignatureOfNonce().isEmpty() ? null : proto.getAccountAgeWitnessSignatureOfNonce().toByteArray()); ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessNonce()),
ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfNonce()));
} }
@ -179,6 +188,7 @@ public final class PublishDepositTxRequest extends TradeMessage implements Mailb
",\n makerInputs=" + makerInputs + ",\n makerInputs=" + makerInputs +
",\n senderNodeAddress=" + senderNodeAddress + ",\n senderNodeAddress=" + senderNodeAddress +
",\n uid='" + uid + '\'' + ",\n uid='" + uid + '\'' +
",\n accountAgeWitnessSignatureOfAccountData=" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfAccountData) +
",\n accountAgeWitnessNonce=" + Utilities.bytesAsHexString(accountAgeWitnessNonce) + ",\n accountAgeWitnessNonce=" + Utilities.bytesAsHexString(accountAgeWitnessNonce) +
",\n accountAgeWitnessSignatureOfNonce=" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfNonce) + ",\n accountAgeWitnessSignatureOfNonce=" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfNonce) +
"\n} " + super.toString(); "\n} " + super.toString();

View file

@ -127,7 +127,6 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
TakerProcessPublishDepositTxRequest.class, TakerProcessPublishDepositTxRequest.class,
CheckIfPeerIsBanned.class, CheckIfPeerIsBanned.class,
TakerVerifyMakerAccount.class, TakerVerifyMakerAccount.class,
TakerVerifyOffersAccountAgeWitnessHash.class,
VerifyPeersAccountAgeWitness.class, VerifyPeersAccountAgeWitness.class,
TakerVerifyMakerFeePayment.class, TakerVerifyMakerFeePayment.class,
TakerVerifyAndSignContract.class, TakerVerifyAndSignContract.class,

View file

@ -29,7 +29,6 @@ import io.bisq.core.trade.messages.TradeMessage;
import io.bisq.core.trade.protocol.tasks.CheckIfPeerIsBanned; import io.bisq.core.trade.protocol.tasks.CheckIfPeerIsBanned;
import io.bisq.core.trade.protocol.tasks.PublishAccountAgeWitness; import io.bisq.core.trade.protocol.tasks.PublishAccountAgeWitness;
import io.bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness; import io.bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
import io.bisq.core.trade.protocol.tasks.taker.TakerVerifyOffersAccountAgeWitnessHash;
import io.bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx; import io.bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx;
import io.bisq.core.trade.protocol.tasks.seller.SellerProcessCounterCurrencyTransferStartedMessage; import io.bisq.core.trade.protocol.tasks.seller.SellerProcessCounterCurrencyTransferStartedMessage;
import io.bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage; import io.bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage;
@ -123,7 +122,6 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
TakerProcessPublishDepositTxRequest.class, TakerProcessPublishDepositTxRequest.class,
CheckIfPeerIsBanned.class, CheckIfPeerIsBanned.class,
TakerVerifyMakerAccount.class, TakerVerifyMakerAccount.class,
TakerVerifyOffersAccountAgeWitnessHash.class,
VerifyPeersAccountAgeWitness.class, VerifyPeersAccountAgeWitness.class,
TakerVerifyMakerFeePayment.class, TakerVerifyMakerFeePayment.class,
TakerVerifyAndSignContract.class, TakerVerifyAndSignContract.class,

View file

@ -60,6 +60,10 @@ public final class TradingPeer implements PersistablePayload {
private long changeOutputValue; private long changeOutputValue;
@Nullable @Nullable
private String changeOutputAddress; private String changeOutputAddress;
// added in v 0.6
@Nullable
private byte[] accountAgeWitnessSignatureOfAccountData;
@Nullable @Nullable
private byte[] accountAgeWitnessNonce; private byte[] accountAgeWitnessNonce;
@Nullable @Nullable
@ -82,6 +86,7 @@ public final class TradingPeer implements PersistablePayload {
Optional.ofNullable(multiSigPubKey).ifPresent(e -> builder.setMultiSigPubKey(ByteString.copyFrom(e))); Optional.ofNullable(multiSigPubKey).ifPresent(e -> builder.setMultiSigPubKey(ByteString.copyFrom(e)));
Optional.ofNullable(rawTransactionInputs).ifPresent(e -> builder.addAllRawTransactionInputs(ProtoUtil.collectionToProto(e))); Optional.ofNullable(rawTransactionInputs).ifPresent(e -> builder.addAllRawTransactionInputs(ProtoUtil.collectionToProto(e)));
Optional.ofNullable(changeOutputAddress).ifPresent(builder::setChangeOutputAddress); Optional.ofNullable(changeOutputAddress).ifPresent(builder::setChangeOutputAddress);
Optional.ofNullable(accountAgeWitnessSignatureOfAccountData).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfAccountData(ByteString.copyFrom(e)));
Optional.ofNullable(accountAgeWitnessNonce).ifPresent(e -> builder.setAccountAgeWitnessNonce(ByteString.copyFrom(e))); Optional.ofNullable(accountAgeWitnessNonce).ifPresent(e -> builder.setAccountAgeWitnessNonce(ByteString.copyFrom(e)));
Optional.ofNullable(accountAgeWitnessSignatureOfNonce).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfNonce(ByteString.copyFrom(e))); Optional.ofNullable(accountAgeWitnessSignatureOfNonce).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfNonce(ByteString.copyFrom(e)));
return builder.build(); return builder.build();
@ -108,6 +113,7 @@ public final class TradingPeer implements PersistablePayload {
.collect(Collectors.toList()); .collect(Collectors.toList());
tradingPeer.setRawTransactionInputs(rawTransactionInputs); tradingPeer.setRawTransactionInputs(rawTransactionInputs);
tradingPeer.setChangeOutputAddress(ProtoUtil.stringOrNullFromProto(proto.getChangeOutputAddress())); tradingPeer.setChangeOutputAddress(ProtoUtil.stringOrNullFromProto(proto.getChangeOutputAddress()));
tradingPeer.setAccountAgeWitnessNonce(ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfAccountData()));
tradingPeer.setAccountAgeWitnessNonce(ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessNonce())); tradingPeer.setAccountAgeWitnessNonce(ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessNonce()));
tradingPeer.setAccountAgeWitnessSignatureOfNonce(ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfNonce())); tradingPeer.setAccountAgeWitnessSignatureOfNonce(ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfNonce()));
return tradingPeer; return tradingPeer;

View file

@ -32,7 +32,7 @@ public class PublishAccountAgeWitness extends TradeTask {
protected void run() { protected void run() {
try { try {
runInterceptHook(); runInterceptHook();
processModel.getAccountAgeWitnessService().publishAccountAgeWitness(processModel.getPaymentAccountPayload(trade)); processModel.getAccountAgeWitnessService().publishMyAccountAgeWitness(processModel.getPaymentAccountPayload(trade));
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {
failed(t); failed(t);

View file

@ -19,10 +19,12 @@ package io.bisq.core.trade.protocol.tasks;
import io.bisq.common.crypto.PubKeyRing; import io.bisq.common.crypto.PubKeyRing;
import io.bisq.common.taskrunner.TaskRunner; import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.offer.Offer;
import io.bisq.core.payment.AccountAgeWitness; import io.bisq.core.payment.AccountAgeWitness;
import io.bisq.core.payment.AccountAgeWitnessService; import io.bisq.core.payment.AccountAgeWitnessService;
import io.bisq.core.payment.payload.PaymentAccountPayload; import io.bisq.core.payment.payload.PaymentAccountPayload;
import io.bisq.core.trade.Trade; import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.TradingPeer;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.Date; import java.util.Date;
@ -45,31 +47,33 @@ public class VerifyPeersAccountAgeWitness extends TradeTask {
runInterceptHook(); runInterceptHook();
final AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService(); final AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService();
final PaymentAccountPayload peersPaymentAccountPayload = checkNotNull(processModel.getTradingPeer().getPaymentAccountPayload(), final TradingPeer tradingPeer = processModel.getTradingPeer();
final PaymentAccountPayload peersPaymentAccountPayload = checkNotNull(tradingPeer.getPaymentAccountPayload(),
"Peers peersPaymentAccountPayload must not be null"); "Peers peersPaymentAccountPayload must not be null");
final PubKeyRing peersPubKeyRing = checkNotNull(tradingPeer.getPubKeyRing(), "peersPubKeyRing must not be null");
final String[] errorMsg1 = new String[1]; final Offer offer = trade.getOffer();
boolean result = accountAgeWitnessService.verifyTradeLimit(trade.getOffer(), peersPaymentAccountPayload, errorMessage -> errorMsg1[0] = errorMessage); final Optional<String> accountAgeWitnessHashAsHex = offer.getAccountAgeWitnessHashAsHex();
if (result) { Optional<AccountAgeWitness> witnessOptional = accountAgeWitnessHashAsHex.isPresent() ?
byte[] nonce = processModel.getTradingPeer().getAccountAgeWitnessNonce(); accountAgeWitnessService.getPeersWitnessByHashAsHex(accountAgeWitnessHashAsHex.get())
byte[] signatureOfNonce = processModel.getTradingPeer().getAccountAgeWitnessSignatureOfNonce(); : Optional.<AccountAgeWitness>empty();
Optional<AccountAgeWitness> witnessOptional = accountAgeWitnessService.getWitnessByPaymentAccountPayload(peersPaymentAccountPayload); byte[] nonce = tradingPeer.getAccountAgeWitnessNonce();
byte[] signatureOfNonce = tradingPeer.getAccountAgeWitnessSignatureOfNonce();
if (witnessOptional.isPresent() && nonce != null && signatureOfNonce != null) { if (witnessOptional.isPresent() && nonce != null && signatureOfNonce != null) {
AccountAgeWitness witness = witnessOptional.get(); AccountAgeWitness witness = witnessOptional.get();
final PubKeyRing pubKeyRing = processModel.getTradingPeer().getPubKeyRing(); final String[] errorMsg = new String[1];
checkNotNull(pubKeyRing, "processModel.getTradingPeer().getPubKeyRing() must not be null"); byte[] peersSignatureOfAccountHash = tradingPeer.getAccountAgeWitnessSignatureOfAccountData();
final String[] errorMsg2 = new String[1]; boolean result = accountAgeWitnessService.verifyPeersAccountAgeWitness(offer,
result = accountAgeWitnessService.verifyAccountAgeWitness(peersPaymentAccountPayload.getAgeWitnessInputData(), peersPaymentAccountPayload,
witness, witness,
peersPaymentAccountPayload.getSalt(), peersPubKeyRing,
pubKeyRing.getSignaturePubKey(), peersSignatureOfAccountHash,
nonce, nonce,
signatureOfNonce, signatureOfNonce,
errorMessage -> errorMsg2[0] = errorMessage); errorMessage -> errorMsg[0] = errorMessage);
if (result) if (result)
complete(); complete();
else else
failed(errorMsg2[0]); failed(errorMsg[0]);
} else { } else {
String msg = !witnessOptional.isPresent() ? String msg = !witnessOptional.isPresent() ?
"Peers AccountAgeWitness is not found." : "Peers AccountAgeWitness is not found." :
@ -80,15 +84,10 @@ public class VerifyPeersAccountAgeWitness extends TradeTask {
log.error(msg); log.error(msg);
failed(msg); failed(msg);
} else { } else {
log.warn(msg + "\nWe tolerate that until 1. of Feb. 2018"); log.warn(msg + "\nWe tolerate offers without account age witness until first of Feb. 2018");
complete(); complete();
} }
} }
} else {
String msg = "The offer verification failed.\nReason: " + errorMsg1[0];
log.error(msg);
failed(msg);
}
} catch (Throwable t) { } catch (Throwable t) {
failed(t); failed(t);
} }

View file

@ -66,6 +66,7 @@ public class MakerProcessPayDepositRequest extends TradeTask {
if (payDepositRequest.getAcceptedArbitratorNodeAddresses().isEmpty()) if (payDepositRequest.getAcceptedArbitratorNodeAddresses().isEmpty())
failed("acceptedArbitratorNames must not be empty"); failed("acceptedArbitratorNames must not be empty");
processModel.getTradingPeer().setAccountAgeWitnessSignatureOfAccountData(payDepositRequest.getAccountAgeWitnessSignatureOfAccountData());
final byte[] accountAgeWitnessNonce = payDepositRequest.getAccountAgeWitnessNonce(); final byte[] accountAgeWitnessNonce = payDepositRequest.getAccountAgeWitnessNonce();
processModel.getTradingPeer().setAccountAgeWitnessNonce(accountAgeWitnessNonce); processModel.getTradingPeer().setAccountAgeWitnessNonce(accountAgeWitnessNonce);
processModel.getTradingPeer().setAccountAgeWitnessSignatureOfNonce(payDepositRequest.getAccountAgeWitnessSignatureOfNonce()); processModel.getTradingPeer().setAccountAgeWitnessSignatureOfNonce(payDepositRequest.getAccountAgeWitnessSignatureOfNonce());

View file

@ -21,6 +21,7 @@ import io.bisq.common.crypto.Sig;
import io.bisq.common.taskrunner.TaskRunner; import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.btc.AddressEntry; import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.wallet.BtcWalletService; import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.payment.payload.PaymentAccountPayload;
import io.bisq.core.trade.Trade; import io.bisq.core.trade.Trade;
import io.bisq.core.trade.messages.PublishDepositTxRequest; import io.bisq.core.trade.messages.PublishDepositTxRequest;
import io.bisq.core.trade.protocol.tasks.TradeTask; import io.bisq.core.trade.protocol.tasks.TradeTask;
@ -32,6 +33,7 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j @Slf4j
public class MakerSendPublishDepositTxRequest extends TradeTask { public class MakerSendPublishDepositTxRequest extends TradeTask {
@ -59,12 +61,17 @@ public class MakerSendPublishDepositTxRequest extends TradeTask {
// Maker has to use preparedDepositTx as nonce. // Maker has to use preparedDepositTx as nonce.
// He cannot manipulate the preparedDepositTx - so we avoid to have a challenge protocol for passing the nonce we want to get signed. // He cannot manipulate the preparedDepositTx - so we avoid to have a challenge protocol for passing the nonce we want to get signed.
final PaymentAccountPayload paymentAccountPayload = checkNotNull(processModel.getPaymentAccountPayload(trade), "processModel.getPaymentAccountPayload(trade) must not be null");
byte[] accountAgeWitnessSignatureOfAccountData = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(),
processModel.getAccountAgeWitnessService().getAccountInputDataWithSalt(paymentAccountPayload));
//noinspection UnnecessaryLocalVariable
byte[] accountAgeWitnessNonce = preparedDepositTx; byte[] accountAgeWitnessNonce = preparedDepositTx;
byte[] accountAgeWitnessSignatureOfNonce = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), accountAgeWitnessNonce); byte[] accountAgeWitnessSignatureOfNonce = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), accountAgeWitnessNonce);
PublishDepositTxRequest message = new PublishDepositTxRequest( PublishDepositTxRequest message = new PublishDepositTxRequest(
processModel.getOfferId(), processModel.getOfferId(),
processModel.getPaymentAccountPayload(trade), paymentAccountPayload,
processModel.getAccountId(), processModel.getAccountId(),
makerMultiSigPubKey, makerMultiSigPubKey,
trade.getContractAsJson(), trade.getContractAsJson(),
@ -74,6 +81,7 @@ public class MakerSendPublishDepositTxRequest extends TradeTask {
processModel.getRawTransactionInputs(), processModel.getRawTransactionInputs(),
processModel.getMyNodeAddress(), processModel.getMyNodeAddress(),
UUID.randomUUID().toString(), UUID.randomUUID().toString(),
accountAgeWitnessSignatureOfAccountData,
accountAgeWitnessNonce, accountAgeWitnessNonce,
accountAgeWitnessSignatureOfNonce); accountAgeWitnessSignatureOfNonce);

View file

@ -54,7 +54,7 @@ public class MakerSetupDepositTxListener extends TradeTask {
if (walletService.getBalanceForAddress(address).isZero()) { if (walletService.getBalanceForAddress(address).isZero()) {
trade.setState(Trade.State.MAKER_SAW_DEPOSIT_TX_IN_NETWORK); trade.setState(Trade.State.MAKER_SAW_DEPOSIT_TX_IN_NETWORK);
swapReservedForTradeEntry(); swapReservedForTradeEntry();
processModel.getAccountAgeWitnessService().publishAccountAgeWitness(processModel.getPaymentAccountPayload(trade)); processModel.getAccountAgeWitnessService().publishMyAccountAgeWitness(processModel.getPaymentAccountPayload(trade));
} else { } else {
listener = new BalanceListener(address) { listener = new BalanceListener(address) {
@Override @Override
@ -62,7 +62,7 @@ public class MakerSetupDepositTxListener extends TradeTask {
if (balance.isZero() && trade.getState().getPhase() == Trade.Phase.TAKER_FEE_PUBLISHED) { if (balance.isZero() && trade.getState().getPhase() == Trade.Phase.TAKER_FEE_PUBLISHED) {
trade.setState(Trade.State.MAKER_SAW_DEPOSIT_TX_IN_NETWORK); trade.setState(Trade.State.MAKER_SAW_DEPOSIT_TX_IN_NETWORK);
swapReservedForTradeEntry(); swapReservedForTradeEntry();
processModel.getAccountAgeWitnessService().publishAccountAgeWitness(processModel.getPaymentAccountPayload(trade)); processModel.getAccountAgeWitnessService().publishMyAccountAgeWitness(processModel.getPaymentAccountPayload(trade));
} }
} }
}; };

View file

@ -56,6 +56,7 @@ public class TakerProcessPublishDepositTxRequest extends TradeTask {
final byte[] preparedDepositTx = publishDepositTxRequest.getPreparedDepositTx(); final byte[] preparedDepositTx = publishDepositTxRequest.getPreparedDepositTx();
processModel.setPreparedDepositTx(checkNotNull(preparedDepositTx)); processModel.setPreparedDepositTx(checkNotNull(preparedDepositTx));
processModel.getTradingPeer().setAccountAgeWitnessSignatureOfAccountData(publishDepositTxRequest.getAccountAgeWitnessSignatureOfAccountData());
final byte[] accountAgeWitnessNonce = publishDepositTxRequest.getAccountAgeWitnessNonce(); final byte[] accountAgeWitnessNonce = publishDepositTxRequest.getAccountAgeWitnessNonce();
processModel.getTradingPeer().setAccountAgeWitnessNonce(accountAgeWitnessNonce); processModel.getTradingPeer().setAccountAgeWitnessNonce(accountAgeWitnessNonce);
processModel.getTradingPeer().setAccountAgeWitnessSignatureOfNonce(publishDepositTxRequest.getAccountAgeWitnessSignatureOfNonce()); processModel.getTradingPeer().setAccountAgeWitnessSignatureOfNonce(publishDepositTxRequest.getAccountAgeWitnessSignatureOfNonce());

View file

@ -22,6 +22,7 @@ import io.bisq.common.crypto.Sig;
import io.bisq.common.taskrunner.TaskRunner; import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.btc.AddressEntry; import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.wallet.BtcWalletService; import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.payment.payload.PaymentAccountPayload;
import io.bisq.core.trade.Trade; import io.bisq.core.trade.Trade;
import io.bisq.core.trade.messages.PayDepositRequest; import io.bisq.core.trade.messages.PayDepositRequest;
import io.bisq.core.trade.protocol.tasks.TradeTask; import io.bisq.core.trade.protocol.tasks.TradeTask;
@ -73,6 +74,9 @@ public class TakerSendPayDepositRequest extends TradeTask {
// Taker has to use offerId as nonce (he cannot manipulate that - so we avoid to have a challenge protocol for passing the nonce we want to get signed) // Taker has to use offerId as nonce (he cannot manipulate that - so we avoid to have a challenge protocol for passing the nonce we want to get signed)
// He cannot manipulate the offerId - so we avoid to have a challenge protocol for passing the nonce we want to get signed. // He cannot manipulate the offerId - so we avoid to have a challenge protocol for passing the nonce we want to get signed.
final PaymentAccountPayload paymentAccountPayload = checkNotNull(processModel.getPaymentAccountPayload(trade), "processModel.getPaymentAccountPayload(trade) must not be null");
byte[] accountAgeWitnessSignatureOfAccountData = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(),
processModel.getAccountAgeWitnessService().getAccountInputDataWithSalt(paymentAccountPayload));
byte[] accountAgeWitnessNonce = offerId.getBytes(); byte[] accountAgeWitnessNonce = offerId.getBytes();
byte[] accountAgeWitnessSignatureOfNonce = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), accountAgeWitnessNonce); byte[] accountAgeWitnessSignatureOfNonce = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), accountAgeWitnessNonce);
@ -90,7 +94,7 @@ public class TakerSendPayDepositRequest extends TradeTask {
takerMultiSigPubKey, takerMultiSigPubKey,
takerPayoutAddressString, takerPayoutAddressString,
processModel.getPubKeyRing(), processModel.getPubKeyRing(),
processModel.getPaymentAccountPayload(trade), paymentAccountPayload,
processModel.getAccountId(), processModel.getAccountId(),
trade.getTakerFeeTxId(), trade.getTakerFeeTxId(),
new ArrayList<>(acceptedArbitratorAddresses), new ArrayList<>(acceptedArbitratorAddresses),
@ -99,6 +103,7 @@ public class TakerSendPayDepositRequest extends TradeTask {
trade.getMediatorNodeAddress(), trade.getMediatorNodeAddress(),
UUID.randomUUID().toString(), UUID.randomUUID().toString(),
Version.getP2PMessageVersion(), Version.getP2PMessageVersion(),
accountAgeWitnessSignatureOfAccountData,
accountAgeWitnessNonce, accountAgeWitnessNonce,
accountAgeWitnessSignatureOfNonce); accountAgeWitnessSignatureOfNonce);

View file

@ -1,75 +0,0 @@
/*
* 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 io.bisq.core.trade.protocol.tasks.taker;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.common.util.Utilities;
import io.bisq.core.offer.OfferPayload;
import io.bisq.core.payment.payload.PaymentAccountPayload;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j
public class TakerVerifyOffersAccountAgeWitnessHash extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public TakerVerifyOffersAccountAgeWitnessHash(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@Override
protected void run() {
try {
runInterceptHook();
final PaymentAccountPayload peersPaymentAccountPayload = checkNotNull(processModel.getTradingPeer().getPaymentAccountPayload(),
"Peers paymentAccountPayload must not be null");
final Map<String, String> extraDataMap = trade.getOffer().getExtraDataMap();
if (extraDataMap != null && extraDataMap.containsKey(OfferPayload.ACCOUNT_AGE_WITNESS_HASH)) {
final String[] errorMsg2 = new String[1];
boolean result = processModel.getAccountAgeWitnessService().verifyOffersAccountAgeWitness(peersPaymentAccountPayload,
Utilities.decodeFromHex(extraDataMap.get(OfferPayload.ACCOUNT_AGE_WITNESS_HASH)),
errorMessage -> errorMsg2[0] = errorMessage);
if (result)
complete();
else
failed(errorMsg2[0]);
} else {
String msg = "ACCOUNT_AGE_WITNESS_HASH is not found in offer.\nThat is expected for offers created before v.0.6." +
"\nTrade ID=" + trade.getId();
if (new Date().after(new GregorianCalendar(2018, GregorianCalendar.FEBRUARY, 1).getTime())) {
msg = "The offer verification failed.\nReason: " + msg;
log.error(msg);
failed(msg);
} else {
log.warn(msg + "\nWe tolerate that until 1. of Feb. 2018");
complete();
}
}
} catch (Throwable t) {
failed(t);
}
}
}

View file

@ -71,14 +71,6 @@ public class AccountAgeWitnessServiceTest {
assertFalse(service.isTradeDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate, errorMessage -> {})); assertFalse(service.isTradeDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate, errorMessage -> {}));
} }
@Test
public void testVerifySigPubKey() {
byte[] sigPubKeHash = Hash.getSha256Ripemd160hash(Sig.getPublicKeyBytes(publicKey));
assertFalse(service.verifySigPubKeyHash(new byte[0], publicKey, errorMessage -> {}));
assertFalse(service.verifySigPubKeyHash(new byte[1], publicKey, errorMessage -> {}));
assertTrue(service.verifySigPubKeyHash(sigPubKeHash, publicKey, errorMessage -> {}));
}
@Test @Test
public void testVerifySignature() throws CryptoException { public void testVerifySignature() throws CryptoException {
byte[] ageWitnessInputData = "test".getBytes(Charset.forName("UTF-8")); byte[] ageWitnessInputData = "test".getBytes(Charset.forName("UTF-8"));

View file

@ -50,14 +50,14 @@ public class PeerInfoIcon extends Group {
peerTagMap = preferences.getPeerTagMap(); peerTagMap = preferences.getPeerTagMap();
boolean hasTraded = numTrades > 0; boolean hasTraded = numTrades > 0;
String accountAge = formatter.formatAccountAge(accountAgeWitnessService.getAccountAge(offer)); String accountAge = formatter.formatAccountAge(accountAgeWitnessService.getPeersAccountAge(offer));
tooltipText = hasTraded ? tooltipText = hasTraded ?
Res.get("peerInfoIcon.tooltip.trade.traded", role, hostName, numTrades, accountAge) : Res.get("peerInfoIcon.tooltip.trade.traded", role, hostName, numTrades, accountAge) :
Res.get("peerInfoIcon.tooltip.trade.notTraded", role, hostName, accountAge); Res.get("peerInfoIcon.tooltip.trade.notTraded", role, hostName, accountAge);
// outer circle // outer circle
Color ringColor; Color ringColor;
switch (accountAgeWitnessService.getAccountAgeCategory(accountAgeWitnessService.getAccountAge(offer))) { switch (accountAgeWitnessService.getAccountAgeCategory(accountAgeWitnessService.getPeersAccountAge(offer))) {
case TWO_MONTHS_OR_MORE: case TWO_MONTHS_OR_MORE:
ringColor = Color.rgb(0, 225, 0); // > 2 months green ringColor = Color.rgb(0, 225, 0); // > 2 months green
break; break;

View file

@ -151,10 +151,10 @@ public abstract class PaymentMethodForm {
CurrencyUtil.getDefaultTradeCurrency(); CurrencyUtil.getDefaultTradeCurrency();
final boolean isAddAccountScreen = paymentAccount.getAccountName() == null; final boolean isAddAccountScreen = paymentAccount.getAccountName() == null;
final long accountAge = !isAddAccountScreen ? accountAgeWitnessService.getAccountAge(paymentAccount.getPaymentAccountPayload()) : 0L; final long accountAge = !isAddAccountScreen ? accountAgeWitnessService.getMyAccountAge(paymentAccount.getPaymentAccountPayload()) : 0L;
addLabelTextField(gridPane, ++gridRow, Res.get("payment.limitations"), Res.get("payment.maxPeriodAndLimit", addLabelTextField(gridPane, ++gridRow, Res.get("payment.limitations"), Res.get("payment.maxPeriodAndLimit",
getTimeText(hours), getTimeText(hours),
formatter.formatCoinWithCode(Coin.valueOf(accountAgeWitnessService.getTradeLimit(paymentAccount, tradeCurrency.getCode()))), formatter.formatCoinWithCode(Coin.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrency.getCode()))),
formatter.formatAccountAge(accountAge))); formatter.formatAccountAge(accountAge)));
if (isAddAccountScreen) { if (isAddAccountScreen) {

View file

@ -1199,12 +1199,12 @@ public class MainViewModel implements ViewModel {
user.addPaymentAccount(okPayAccount); user.addPaymentAccount(okPayAccount);
if (p2PService.isBootstrapped()) { if (p2PService.isBootstrapped()) {
accountAgeWitnessService.publishAccountAgeWitness(okPayAccount.getPaymentAccountPayload()); accountAgeWitnessService.publishMyAccountAgeWitness(okPayAccount.getPaymentAccountPayload());
} else { } else {
p2PService.addP2PServiceListener(new BootstrapListener() { p2PService.addP2PServiceListener(new BootstrapListener() {
@Override @Override
public void onBootstrapComplete() { public void onBootstrapComplete() {
accountAgeWitnessService.publishAccountAgeWitness(okPayAccount.getPaymentAccountPayload()); accountAgeWitnessService.publishMyAccountAgeWitness(okPayAccount.getPaymentAccountPayload());
} }
}); });
} }

View file

@ -115,7 +115,7 @@ class AltCoinAccountsDataModel extends ActivatableDataModel {
}); });
} }
accountAgeWitnessService.publishAccountAgeWitness(paymentAccount.getPaymentAccountPayload()); accountAgeWitnessService.publishMyAccountAgeWitness(paymentAccount.getPaymentAccountPayload());
} }
public boolean onDeleteAccount(PaymentAccount paymentAccount) { public boolean onDeleteAccount(PaymentAccount paymentAccount) {

View file

@ -116,7 +116,7 @@ class FiatAccountsDataModel extends ActivatableDataModel {
}); });
} }
accountAgeWitnessService.publishAccountAgeWitness(paymentAccount.getPaymentAccountPayload()); accountAgeWitnessService.publishMyAccountAgeWitness(paymentAccount.getPaymentAccountPayload());
} }
public boolean onDeleteAccount(PaymentAccount paymentAccount) { public boolean onDeleteAccount(PaymentAccount paymentAccount) {

View file

@ -180,7 +180,7 @@ public class LockedView extends ActivatableView<VBox, Void> {
} else if (openOfferManager.getOpenOfferById(offerId).isPresent()) { } else if (openOfferManager.getOpenOfferById(offerId).isPresent()) {
return Optional.of(openOfferManager.getOpenOfferById(offerId).get()); return Optional.of(openOfferManager.getOpenOfferById(offerId).get());
} else { } else {
return Optional.empty(); return Optional.<Tradable>empty();
} }
} }

View file

@ -180,7 +180,7 @@ public class ReservedView extends ActivatableView<VBox, Void> {
} else if (openOfferManager.getOpenOfferById(offerId).isPresent()) { } else if (openOfferManager.getOpenOfferById(offerId).isPresent()) {
return Optional.of(openOfferManager.getOpenOfferById(offerId).get()); return Optional.of(openOfferManager.getOpenOfferById(offerId).get());
} else { } else {
return Optional.empty(); return Optional.<Tradable>empty();
} }
} }

View file

@ -401,7 +401,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
} else if (failedTradesManager.getTradeById(offerId).isPresent()) { } else if (failedTradesManager.getTradeById(offerId).isPresent()) {
return Optional.of(failedTradesManager.getTradeById(offerId).get()); return Optional.of(failedTradesManager.getTradeById(offerId).get());
} else { } else {
return Optional.empty(); return Optional.<Tradable>empty();
} }
} }

View file

@ -347,8 +347,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
HashMap<String, String> extraDataMap = new HashMap<>(); HashMap<String, String> extraDataMap = new HashMap<>();
if (CurrencyUtil.isFiatCurrency(currencyCode)) { if (CurrencyUtil.isFiatCurrency(currencyCode)) {
final String hashOfPaymentAccountAsHex = accountAgeWitnessService.getWitnessHashAsHex(paymentAccount.getPaymentAccountPayload()); final String myWitnessHashAsHex = accountAgeWitnessService.getMyWitnessHashAsHex(paymentAccount.getPaymentAccountPayload());
extraDataMap.put(OfferPayload.ACCOUNT_AGE_WITNESS_HASH, hashOfPaymentAccountAsHex); extraDataMap.put(OfferPayload.ACCOUNT_AGE_WITNESS_HASH, myWitnessHashAsHex);
} }
Coin buyerSecurityDepositAsCoin = buyerSecurityDeposit.get(); Coin buyerSecurityDepositAsCoin = buyerSecurityDeposit.get();
@ -553,7 +553,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
long getMaxTradeLimit() { long getMaxTradeLimit() {
if (paymentAccount != null) if (paymentAccount != null)
return accountAgeWitnessService.getTradeLimit(paymentAccount, tradeCurrencyCode.get()); return accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrencyCode.get());
else else
return 0; return 0;
} }

View file

@ -401,10 +401,10 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
} else if (isInsufficientTradeLimit) { } else if (isInsufficientTradeLimit) {
final Optional<PaymentAccount> account = model.getMostMaturePaymentAccountForOffer(offer); final Optional<PaymentAccount> account = model.getMostMaturePaymentAccountForOffer(offer);
if (account.isPresent()) { if (account.isPresent()) {
final long tradeLimit = model.accountAgeWitnessService.getTradeLimit(account.get(), offer.getCurrencyCode()); final long tradeLimit = model.accountAgeWitnessService.getMyTradeLimit(account.get(), offer.getCurrencyCode());
new Popup<>() new Popup<>()
.warning(Res.get("offerbook.warning.tradeLimitNotMatching", .warning(Res.get("offerbook.warning.tradeLimitNotMatching",
formatter.formatAccountAge(model.accountAgeWitnessService.getAccountAge(offer)), formatter.formatAccountAge(model.accountAgeWitnessService.getPeersAccountAge(offer)),
formatter.formatCoinWithCode(Coin.valueOf(tradeLimit)), formatter.formatCoinWithCode(Coin.valueOf(tradeLimit)),
formatter.formatCoinWithCode(offer.getMinAmount()))) formatter.formatCoinWithCode(offer.getMinAmount())))
.show(); .show();

View file

@ -464,7 +464,7 @@ class OfferBookViewModel extends ActivatableViewModel {
boolean isInsufficientTradeLimit(Offer offer) { boolean isInsufficientTradeLimit(Offer offer) {
Optional<PaymentAccount> accountOptional = getMostMaturePaymentAccountForOffer(offer); Optional<PaymentAccount> accountOptional = getMostMaturePaymentAccountForOffer(offer);
final long myTradeLimit = accountOptional.isPresent() ? accountAgeWitnessService.getTradeLimit(accountOptional.get(), offer.getCurrencyCode()) : 0L; final long myTradeLimit = accountOptional.isPresent() ? accountAgeWitnessService.getMyTradeLimit(accountOptional.get(), offer.getCurrencyCode()) : 0L;
final long offerMinAmount = offer.getMinAmount().value; final long offerMinAmount = offer.getMinAmount().value;
log.debug("isInsufficientTradeLimit accountOptional={}, myTradeLimit={}, offerMinAmount={}, ", log.debug("isInsufficientTradeLimit accountOptional={}, myTradeLimit={}, offerMinAmount={}, ",
accountOptional.isPresent() ? accountOptional.get().getAccountName() : "null", accountOptional.isPresent() ? accountOptional.get().getAccountName() : "null",

View file

@ -362,7 +362,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
long getMaxTradeLimit() { long getMaxTradeLimit() {
if (paymentAccount != null) if (paymentAccount != null)
return accountAgeWitnessService.getTradeLimit(paymentAccount, getCurrencyCode()); return accountAgeWitnessService.getMyTradeLimit(paymentAccount, getCurrencyCode());
else else
return 0; return 0;
} }

View file

@ -119,8 +119,8 @@ public abstract class Overlay<T extends Overlay> {
protected Pane owner; protected Pane owner;
protected GridPane gridPane; protected GridPane gridPane;
protected Button closeButton; protected Button closeButton;
protected Optional<Runnable> closeHandlerOptional = Optional.empty(); protected Optional<Runnable> closeHandlerOptional = Optional.<Runnable>empty();
protected Optional<Runnable> actionHandlerOptional = Optional.empty(); protected Optional<Runnable> actionHandlerOptional = Optional.<Runnable>empty();
protected Stage stage; protected Stage stage;
protected boolean showReportErrorButtons; protected boolean showReportErrorButtons;
protected Label messageLabel; protected Label messageLabel;

View file

@ -66,7 +66,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
private final BtcWalletService walletService; private final BtcWalletService walletService;
private final TradeWalletService tradeWalletService; private final TradeWalletService tradeWalletService;
private Dispute dispute; private Dispute dispute;
private Optional<Runnable> finalizeDisputeHandlerOptional = Optional.empty(); private Optional<Runnable> finalizeDisputeHandlerOptional = Optional.<Runnable>empty();
private ToggleGroup tradeAmountToggleGroup, reasonToggleGroup; private ToggleGroup tradeAmountToggleGroup, reasonToggleGroup;
private DisputeResult disputeResult; private DisputeResult disputeResult;
private RadioButton buyerGetsTradeAmountRadioButton, sellerGetsTradeAmountRadioButton, private RadioButton buyerGetsTradeAmountRadioButton, sellerGetsTradeAmountRadioButton,

View file

@ -65,8 +65,8 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
private Offer offer; private Offer offer;
private Coin tradeAmount; private Coin tradeAmount;
private Price tradePrice; private Price tradePrice;
private Optional<Runnable> placeOfferHandlerOptional = Optional.empty(); private Optional<Runnable> placeOfferHandlerOptional = Optional.<Runnable>empty();
private Optional<Runnable> takeOfferHandlerOptional = Optional.empty(); private Optional<Runnable> takeOfferHandlerOptional = Optional.<Runnable>empty();
private BusyAnimation busyAnimation; private BusyAnimation busyAnimation;

View file

@ -79,7 +79,7 @@ public class BisqInstaller {
try { try {
return Optional.of(downloadFiles(fileDescriptors, Utilities.getDownloadOfHomeDir())); return Optional.of(downloadFiles(fileDescriptors, Utilities.getDownloadOfHomeDir()));
} catch (IOException exception) { } catch (IOException exception) {
return Optional.empty(); return Optional.<DownloadTask>empty();
} }
} }

View file

@ -350,9 +350,9 @@ public class SellerStep3View extends TradeStepView {
else if (paymentAccountPayload instanceof SepaAccountPayload) else if (paymentAccountPayload instanceof SepaAccountPayload)
return Optional.of(((SepaAccountPayload) paymentAccountPayload).getHolderName()); return Optional.of(((SepaAccountPayload) paymentAccountPayload).getHolderName());
else else
return Optional.empty(); return Optional.<String>empty();
} else { } else {
return Optional.empty(); return Optional.<String>empty();
} }
} }
} }

View file

@ -103,7 +103,7 @@ public class Connection implements MessageListener {
private OutputStream protoOutputStream; private OutputStream protoOutputStream;
// mutable data, set from other threads but not changed internally. // mutable data, set from other threads but not changed internally.
private Optional<NodeAddress> peersNodeAddressOptional = Optional.empty(); private Optional<NodeAddress> peersNodeAddressOptional = Optional.<NodeAddress>empty();
private volatile boolean stopped; private volatile boolean stopped;
private PeerType peerType; private PeerType peerType;
private final ObjectProperty<NodeAddress> peersNodeAddressProperty = new SimpleObjectProperty<>(); private final ObjectProperty<NodeAddress> peersNodeAddressProperty = new SimpleObjectProperty<>();

View file

@ -56,7 +56,7 @@ public class RequestDataManager implements MessageListener, ConnectionListener,
private final Map<NodeAddress, RequestDataHandler> handlerMap = new HashMap<>(); private final Map<NodeAddress, RequestDataHandler> handlerMap = new HashMap<>();
private final Map<String, GetDataRequestHandler> getDataRequestHandlers = new HashMap<>(); private final Map<String, GetDataRequestHandler> getDataRequestHandlers = new HashMap<>();
private Optional<NodeAddress> nodeAddressOfPreliminaryDataRequest = Optional.empty(); private Optional<NodeAddress> nodeAddressOfPreliminaryDataRequest = Optional.<NodeAddress>empty();
private Timer retryTimer; private Timer retryTimer;
private boolean dataUpdateRequested; private boolean dataUpdateRequested;
private boolean stopped; private boolean stopped;