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()) {
return Optional.of(cryptoCurrencyOptional.get());
} 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 javafx.scene.input.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.bitcoinj.core.Utils;
@ -56,11 +57,11 @@ public class Utilities {
// TODO check out Jackson lib
public static String objectToJson(Object object) {
Gson gson = new GsonBuilder()
.setExclusionStrategies(new AnnotationExclusionStrategy())
.setExclusionStrategies(new AnnotationExclusionStrategy())
/*.excludeFieldsWithModifiers(Modifier.TRANSIENT)*/
/* .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)*/
.setPrettyPrinting()
.create();
.setPrettyPrinting()
.create();
return gson.toJson(object);
}
@ -76,11 +77,11 @@ public class Utilities {
int maximumPoolSize,
long keepAliveTimeInSec) {
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat(name)
.setDaemon(true)
.build();
.setNameFormat(name)
.setDaemon(true)
.build();
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTimeInSec,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(maximumPoolSize), threadFactory);
TimeUnit.SECONDS, new ArrayBlockingQueue<>(maximumPoolSize), threadFactory);
executor.allowCoreThreadTimeOut(true);
executor.setRejectedExecutionHandler((r, e) -> log.debug("RejectedExecutionHandler called"));
return executor;
@ -93,10 +94,10 @@ public class Utilities {
int maximumPoolSize,
long keepAliveTimeInSec) {
final ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat(name)
.setDaemon(true)
.setPriority(Thread.MIN_PRIORITY)
.build();
.setNameFormat(name)
.setDaemon(true)
.setPriority(Thread.MIN_PRIORITY)
.build();
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
executor.setKeepAliveTime(keepAliveTimeInSec, TimeUnit.SECONDS);
executor.allowCoreThreadTimeOut(true);
@ -134,8 +135,8 @@ public class Utilities {
String arch = System.getenv("PROCESSOR_ARCHITECTURE");
String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");
return arch.endsWith("64")
|| wow64Arch != null && wow64Arch.endsWith("64")
? "64" : "32";
|| wow64Arch != null && wow64Arch.endsWith("64")
? "64" : "32";
} else if (osArch.contains("arm")) {
// armv8 is 64 bit, armv7l is 32 bit
return osArch.contains("64") || osArch.contains("v8") ? "64" : "32";
@ -148,12 +149,12 @@ public class Utilities {
public static void printSysInfo() {
log.info("System info: os.name={}; os.version={}; os.arch={}; sun.arch.data.model={}; JRE={}; JVM={}",
System.getProperty("os.name"),
System.getProperty("os.version"),
System.getProperty("os.arch"),
getJVMArchitecture(),
(System.getProperty("java.runtime.version", "-") + " (" + System.getProperty("java.vendor", "-") + ")"),
(System.getProperty("java.vm.version", "-") + " (" + System.getProperty("java.vm.name", "-") + ")")
System.getProperty("os.name"),
System.getProperty("os.version"),
System.getProperty("os.arch"),
getJVMArchitecture(),
(System.getProperty("java.runtime.version", "-") + " (" + System.getProperty("java.vendor", "-") + ")"),
(System.getProperty("java.vm.version", "-") + " (" + System.getProperty("java.vm.name", "-") + ")")
);
}
@ -175,8 +176,8 @@ public class Utilities {
public static void openURI(URI uri) throws IOException {
if (!isLinux()
&& isDesktopSupported()
&& getDesktop().isSupported(Action.BROWSE)) {
&& isDesktopSupported()
&& getDesktop().isSupported(Action.BROWSE)) {
getDesktop().browse(uri);
} else {
// Maybe Application.HostServices works in those cases?
@ -192,8 +193,8 @@ public class Utilities {
public static void openFile(File file) throws IOException {
if (!isLinux()
&& isDesktopSupported()
&& getDesktop().isSupported(Action.OPEN)) {
&& isDesktopSupported()
&& getDesktop().isSupported(Action.OPEN)) {
getDesktop().open(file);
} else {
// Maybe Application.HostServices works in those cases?
@ -258,7 +259,7 @@ public class Utilities {
public static <T> T jsonToObject(String jsonString, Class<T> classOfT) {
Gson gson =
new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create();
new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create();
return gson.fromJson(jsonString, classOfT);
}
@ -372,13 +373,29 @@ public class Utilities {
public static boolean isCtrlPressed(KeyCode keyCode, KeyEvent keyEvent) {
return new KeyCodeCombination(keyCode, KeyCombination.SHORTCUT_DOWN).match(keyEvent) ||
new KeyCodeCombination(keyCode, KeyCombination.CONTROL_DOWN).match(keyEvent);
new KeyCodeCombination(keyCode, KeyCombination.CONTROL_DOWN).match(keyEvent);
}
public static boolean isAltPressed(KeyCode keyCode, KeyEvent 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 {
@Override
public boolean shouldSkipField(FieldAttributes f) {
@ -503,6 +520,6 @@ public class Utilities {
final String name = System.getProperty("java.runtime.name");
final String ver = System.getProperty("java.version");
return name != null && name.equals("Java(TM) SE Runtime Environment")
&& ver != null && (ver.startsWith("1.7") || ver.startsWith("1.8"));
&& ver != null && (ver.startsWith("1.7") || ver.startsWith("1.8"));
}
}

View file

@ -55,7 +55,7 @@ message NetworkEnvelope {
GetBsqBlocksRequest get_bsq_blocks_request = 28;
GetBsqBlocksResponse get_bsq_blocks_response = 29;
NewBsqBlockBroadcastMessage new_bsq_block_broadcast_message = 30;
AddPersistableNetworkPayloadMessage add_persistable_network_payload_message = 31;
}
}
@ -131,10 +131,10 @@ message OfferAvailabilityResponse {
}
message RefreshOfferMessage {
bytes hash_of_data_and_seq_nr = 1;
bytes signature = 2;
bytes hash_of_payload = 3;
int32 sequence_number = 4;
bytes hash_of_data_and_seq_nr = 1;
bytes signature = 2;
bytes hash_of_payload = 3;
int32 sequence_number = 4;
}
@ -195,8 +195,9 @@ message PayDepositRequest {
NodeAddress arbitrator_node_address = 19;
NodeAddress mediator_node_address = 20;
string uid = 21;
bytes account_age_witness_nonce = 22;
bytes account_age_witness_signature_of_nonce = 23;
bytes account_age_witness_signature_of_account_data = 22;
bytes account_age_witness_nonce = 23;
bytes account_age_witness_signature_of_nonce = 24;
}
message PublishDepositTxRequest {
@ -211,8 +212,9 @@ message PublishDepositTxRequest {
bytes maker_multi_sig_pub_key = 9;
NodeAddress sender_node_address = 10;
string uid = 11;
bytes account_age_witness_nonce = 12;
bytes account_age_witness_signature_of_nonce = 13;
bytes account_age_witness_signature_of_account_data = 12;
bytes account_age_witness_nonce = 13;
bytes account_age_witness_signature_of_nonce = 14;
}
message DepositTxPublishedMessage {
@ -498,7 +500,7 @@ message Tx {
int64 burnt_fee = 4;
TxType tx_type = 5;
}
message BsqBlock {
int32 height = 1;
string hash = 2;
@ -560,7 +562,7 @@ message Filter {
bytes owner_pub_key_bytes = 5;
map<string, string> extra_data = 6;
repeated string banned_currencies = 7;
repeated string banned_payment_methods = 8;
repeated string banned_payment_methods = 8;
}
message TradeStatistics {
@ -606,7 +608,7 @@ message OfferPayload {
double market_price_margin = 7;
bool use_market_based_price = 8;
int64 amount = 9;
int64 min_amount = 10;
int64 min_amount = 10;
string base_currency_code = 11;
string counter_currency_code = 12;
repeated NodeAddress arbitrator_node_addresses = 13;
@ -661,9 +663,7 @@ message CompensationRequestPayload {
message AccountAgeWitness {
bytes hash = 1;
bytes sig_pub_key_hash = 2;
bytes signature = 3;
int64 date = 4;
int64 date = 2;
}
@ -926,20 +926,20 @@ message PersistableEnvelope {
PeerList peer_list = 3;
AddressEntryList address_entry_list = 4;
NavigationPath navigation_path = 5;
TradableList tradable_list = 6;
TradeStatisticsList trade_statistics_list = 7;
DisputeList dispute_list = 8;
PreferencesPayload preferences_payload = 9;
UserPayload user_payload = 10;
PaymentAccountList payment_account_list = 11;
// TODO not fully implemented yet
CompensationRequestPayload compensation_request_payload = 12;
VoteItemsList vote_items_list = 13;
BsqChainState bsq_chain_state = 14;
PersistableNetworkPayloadList persistable_network_payload_list = 15;
}
}
@ -1198,8 +1198,9 @@ message TradingPeer {
repeated RawTransactionInput raw_transaction_inputs = 9;
int64 change_output_value = 10;
string change_output_address = 11;
bytes account_age_witness_nonce = 12;
bytes account_age_witness_signature_of_nonce = 13;
bytes account_age_witness_signature_of_account_data = 12;
bytes account_age_witness_nonce = 13;
bytes account_age_witness_signature_of_nonce = 14;
}
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");
sendRequest.coinSelector = new BtcCoinSelector(walletsSetup.getAddressesFromAddressEntries(addressEntries));
Optional<AddressEntry> addressEntryOptional = Optional.empty();
Optional<AddressEntry> addressEntryOptional = Optional.<AddressEntry>empty();
AddressEntry changeAddressAddressEntry = null;
if (changeAddress != null)
addressEntryOptional = findAddressEntry(changeAddress, AddressEntry.Context.AVAILABLE);

View file

@ -105,16 +105,16 @@ public class Offer implements NetworkPayload, PersistablePayload {
public void checkOfferAvailability(OfferAvailabilityModel model, ResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler) {
availabilityProtocol = new OfferAvailabilityProtocol(model,
() -> {
cancelAvailabilityRequest();
resultHandler.handleResult();
},
(errorMessage) -> {
if (availabilityProtocol != null)
availabilityProtocol.cancel();
log.error(errorMessage);
errorMessageHandler.handleErrorMessage(errorMessage);
});
() -> {
cancelAvailabilityRequest();
resultHandler.handleResult();
},
(errorMessage) -> {
if (availabilityProtocol != null)
availabilityProtocol.cancel();
log.error(errorMessage);
errorMessageHandler.handleErrorMessage(errorMessage);
});
availabilityProtocol.sendOfferAvailabilityRequest();
}
@ -134,28 +134,28 @@ public class Offer implements NetworkPayload, PersistablePayload {
double marketPriceMargin = offerPayload.getMarketPriceMargin();
if (CurrencyUtil.isCryptoCurrency(currencyCode)) {
factor = getDirection() == OfferPayload.Direction.SELL ?
1 - marketPriceMargin : 1 + marketPriceMargin;
1 - marketPriceMargin : 1 + marketPriceMargin;
} else {
factor = getDirection() == OfferPayload.Direction.BUY ?
1 - marketPriceMargin : 1 + marketPriceMargin;
1 - marketPriceMargin : 1 + marketPriceMargin;
}
double marketPriceAsDouble = marketPrice.getPrice();
double targetPriceAsDouble = marketPriceAsDouble * factor;
try {
int precision = CurrencyUtil.isCryptoCurrency(currencyCode) ?
Altcoin.SMALLEST_UNIT_EXPONENT :
Fiat.SMALLEST_UNIT_EXPONENT;
Altcoin.SMALLEST_UNIT_EXPONENT :
Fiat.SMALLEST_UNIT_EXPONENT;
double scaled = MathUtils.scaleUpByPowerOf10(targetPriceAsDouble, precision);
final long roundedToLong = MathUtils.roundDoubleToLong(scaled);
return Price.valueOf(currencyCode, roundedToLong);
} catch (Exception e) {
log.error("Exception at getPrice / parseToFiat: " + e.toString() + "\n" +
"That case should never happen.");
"That case should never happen.");
return null;
}
} else {
log.debug("We don't have a market price.\n" +
"That case could only happen if you don't have a price feed.");
"That case could only happen if you don't have a price feed.");
return null;
}
} else {
@ -164,7 +164,7 @@ public class Offer implements NetworkPayload, PersistablePayload {
}
public void checkTradePriceTolerance(long takersTradePrice) throws TradePriceOutOfToleranceException,
MarketPriceNotAvailableException, IllegalArgumentException {
MarketPriceNotAvailableException, IllegalArgumentException {
Price tradePrice = Price.valueOf(getCurrencyCode(), takersTradePrice);
Price offerPrice = getPrice();
if (offerPrice == null)
@ -179,8 +179,8 @@ public class Offer implements NetworkPayload, PersistablePayload {
// from one provider.
if (Math.abs(1 - factor) > 0.01) {
String msg = "Taker's trade price is too far away from our calculated price based on the market price.\n" +
"tradePrice=" + tradePrice.getValue() + "\n" +
"offerPrice=" + offerPrice.getValue();
"tradePrice=" + tradePrice.getValue() + "\n" +
"offerPrice=" + offerPrice.getValue();
log.warn(msg);
throw new TradePriceOutOfToleranceException(msg);
}
@ -270,8 +270,8 @@ public class Offer implements NetworkPayload, PersistablePayload {
public PaymentMethod getPaymentMethod() {
return new PaymentMethod(offerPayload.getPaymentMethodId(),
offerPayload.getMaxTradePeriod(),
Coin.valueOf(offerPayload.getMaxTradeLimit()));
offerPayload.getMaxTradePeriod(),
Coin.valueOf(offerPayload.getMaxTradeLimit()));
}
// utils
@ -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))
return Optional.of(getExtraDataMap().get(OfferPayload.ACCOUNT_AGE_WITNESS_HASH));
else
return Optional.empty();
return Optional.<String>empty();
}
// domain properties
@ -365,8 +365,8 @@ public class Offer implements NetworkPayload, PersistablePayload {
public String getCurrencyCode() {
return CurrencyUtil.isCryptoCurrency(offerPayload.getBaseCurrencyCode()) ?
offerPayload.getBaseCurrencyCode() :
offerPayload.getCounterCurrencyCode();
offerPayload.getBaseCurrencyCode() :
offerPayload.getCounterCurrencyCode();
}
public long getProtocolVersion() {
@ -473,9 +473,9 @@ public class Offer implements NetworkPayload, PersistablePayload {
@Override
public String toString() {
return "Offer{" +
"getErrorMessage()='" + getErrorMessage() + '\'' +
", state=" + getState() +
", offerPayload=" + offerPayload +
'}';
"getErrorMessage()='" + getErrorMessage() + '\'' +
", state=" + getState() +
", offerPayload=" + offerPayload +
'}';
}
}

View file

@ -67,7 +67,7 @@ public final class OfferPayload implements ProtectedStoragePayload, RequiresOwne
}
// Keys for extra map
public static final String ACCOUNT_AGE_WITNESS_HASH = "accountAgeWitness";
public static final String ACCOUNT_AGE_WITNESS_HASH = "accountAgeWitnessHash";
///////////////////////////////////////////////////////////////////////////////////////////
@ -134,11 +134,11 @@ public final class OfferPayload implements ProtectedStoragePayload, RequiresOwne
private final boolean isPrivateOffer;
@Nullable
private final String hashOfChallenge;
// Should be only used in emergency case if we need to add data but do not want to break backward compatibility
// at the P2P network storage checks. The hash of the object will be used to verify if the data is valid. Any new
// field in a class would break that hash and therefore break the storage mechanism.
// extraDataMap used from v0.6 on for hashOfPaymentAccount
// key ACCOUNT_AGE_WITNESS, value: hex string of hashOfPaymentAccount byte array
@Nullable
@ -355,7 +355,7 @@ public final class OfferPayload implements ProtectedStoragePayload, RequiresOwne
// In the offer we support base and counter currency
// Fiat offers have base currency BTC and counterCurrency Fiat
// Altcoins have base currency Altcoin and counterCurrency BTC
// The rest of the app does not support yet that concept of base currency and counter currencies
// The rest of the app does not support yet that concept of base currency and counter currencies
// so we map here for convenience
public String getCurrencyCode() {
return CurrencyUtil.isCryptoCurrency(getBaseCurrencyCode()) ? getBaseCurrencyCode() : getCounterCurrencyCode();

View file

@ -31,31 +31,21 @@ import lombok.extern.slf4j.Slf4j;
import java.util.Date;
import java.util.concurrent.TimeUnit;
// Object has about 94 raw bytes (about 101 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,
// so only the newly added objects since the last release will not be loaded 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)
// Object has about 28 raw bytes (29 bytes is size of PB object)
// 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 be retrieved over the P2P network.
@Slf4j
@Value
public class AccountAgeWitness implements LazyProcessedPayload, PersistableNetworkPayload, PersistableEnvelope, PublishDateVerifiedPayload {
private static final long TOLERANCE = TimeUnit.DAYS.toMillis(1);
private final byte[] hash; // Ripemd160(Sha256(data)) hash 20 bytes
private final byte[] sigPubKeyHash; // Ripemd160(Sha256(sigPubKey)) hash 20 bytes
private final byte[] signature; // about 46 bytes
private final byte[] hash; // Ripemd160(Sha256(concatenated accountHash, signature and sigPubKey)); 20 bytes
private final long date; // 8 byte
public AccountAgeWitness(byte[] hash,
byte[] sigPubKeyHash,
byte[] signature,
long date) {
this.hash = hash;
this.sigPubKeyHash = sigPubKeyHash;
this.signature = signature;
this.date = date;
log.info("new AccountAgeWitness: hash={}, date={} ", Utilities.bytesAsHexString(hash), new Date(date));
}
@ -67,10 +57,8 @@ public class AccountAgeWitness implements LazyProcessedPayload, PersistableNetwo
@Override
public PB.PersistableNetworkPayload toProtoMessage() {
final PB.AccountAgeWitness.Builder builder = PB.AccountAgeWitness.newBuilder()
.setHash(ByteString.copyFrom(hash))
.setSigPubKeyHash(ByteString.copyFrom(sigPubKeyHash))
.setSignature(ByteString.copyFrom(signature))
.setDate(date);
.setHash(ByteString.copyFrom(hash))
.setDate(date);
return PB.PersistableNetworkPayload.newBuilder().setAccountAgeWitness(builder).build();
}
@ -80,10 +68,8 @@ public class AccountAgeWitness implements LazyProcessedPayload, PersistableNetwo
public static AccountAgeWitness fromProto(PB.AccountAgeWitness proto) {
return new AccountAgeWitness(
proto.getHash().toByteArray(),
proto.getSigPubKeyHash().toByteArray(),
proto.getSignature().toByteArray(),
proto.getDate());
proto.getHash().toByteArray(),
proto.getDate());
}
@ -101,13 +87,6 @@ public class AccountAgeWitness implements LazyProcessedPayload, PersistableNetwo
// 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() {
return new P2PDataStorage.ByteArray(hash);
}
@ -115,10 +94,8 @@ public class AccountAgeWitness implements LazyProcessedPayload, PersistableNetwo
@Override
public String toString() {
return "AccountAgeWitness{" +
"\n hash=" + Utilities.bytesAsHexString(hash) +
",\n sigPubKeyHash=" + Utilities.bytesAsHexString(sigPubKeyHash) +
",\n signature=" + Utilities.bytesAsHexString(signature) +
",\n date=" + new Date(date) +
"\n}";
"\n hash=" + Utilities.bytesAsHexString(hash) +
",\n date=" + new Date(date) +
"\n}";
}
}

View file

@ -18,8 +18,8 @@
package io.bisq.core.payment;
import io.bisq.common.crypto.CryptoException;
import io.bisq.common.crypto.Hash;
import io.bisq.common.crypto.KeyRing;
import io.bisq.common.crypto.PubKeyRing;
import io.bisq.common.crypto.Sig;
import io.bisq.common.handlers.ErrorMessageHandler;
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.storage.P2PDataStorage;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
@ -52,12 +51,23 @@ public class AccountAgeWitnessService {
private final P2PService p2PService;
private final Map<P2PDataStorage.ByteArray, AccountAgeWitness> accountAgeWitnessMap = new HashMap<>();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public AccountAgeWitnessService(KeyRing keyRing, P2PService p2PService) {
this.keyRing = keyRing;
this.p2PService = p2PService;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
public void onAllServicesInitialized() {
p2PService.getP2PDataStorage().addPersistableNetworkPayloadMapListener(payload -> {
if (payload instanceof AccountAgeWitness)
@ -71,49 +81,29 @@ public class AccountAgeWitnessService {
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
private void addToMap(AccountAgeWitness accountAgeWitness) {
log.debug("addToMap hash=" + Utilities.bytesAsHexString(accountAgeWitness.getHash()));
if (!accountAgeWitnessMap.containsKey(accountAgeWitness.getHashAsByteArray()))
accountAgeWitnessMap.put(accountAgeWitness.getHashAsByteArray(), accountAgeWitness);
}
public void publishAccountAgeWitness(PaymentAccountPayload paymentAccountPayload) {
try {
AccountAgeWitness accountAgeWitness = getAccountAgeWitness(paymentAccountPayload);
if (!accountAgeWitnessMap.containsKey(accountAgeWitness.getHashAsByteArray()))
p2PService.addPersistableNetworkPayload(accountAgeWitness);
} catch (CryptoException e) {
e.printStackTrace();
log.error(e.toString());
}
public void publishMyAccountAgeWitness(PaymentAccountPayload paymentAccountPayload) {
AccountAgeWitness accountAgeWitness = getMyWitness(paymentAccountPayload);
if (!accountAgeWitnessMap.containsKey(accountAgeWitness.getHashAsByteArray()))
p2PService.addPersistableNetworkPayload(accountAgeWitness);
}
public Optional<AccountAgeWitness> getWitnessByHash(String hashAsHex) {
P2PDataStorage.ByteArray hashAsByteArray = new P2PDataStorage.ByteArray(Utilities.decodeFromHex(hashAsHex));
return accountAgeWitnessMap.containsKey(hashAsByteArray) ? Optional.of(accountAgeWitnessMap.get(hashAsByteArray)) : Optional.<AccountAgeWitness>empty();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Generic
///////////////////////////////////////////////////////////////////////////////////////////
public Optional<AccountAgeWitness> getWitnessByPaymentAccountPayload(PaymentAccountPayload paymentAccountPayload) {
return getWitnessByHash(getWitnessHashAsHex(paymentAccountPayload));
}
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 byte[] getAccountInputDataWithSalt(PaymentAccountPayload paymentAccountPayload) {
return Utilities.concatenateByteArrays(paymentAccountPayload.getAgeWitnessInputData(), paymentAccountPayload.getSalt());
}
public long getAccountAge(AccountAgeWitness accountAgeWitness) {
@ -130,65 +120,17 @@ public class AccountAgeWitnessService {
}
}
private AccountAgeWitness getAccountAgeWitness(PaymentAccountPayload paymentAccountPayload) throws CryptoException {
byte[] hash = getWitnessHash(paymentAccountPayload);
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;
private long getTradeLimit(PaymentMethod paymentMethod, String currencyCode, Optional<AccountAgeWitness> accountAgeWitnessOptional) {
final long maxTradeLimit = paymentMethod.getMaxTradeLimitAsCoin(currencyCode).value;
if (CurrencyUtil.isFiatCurrency(currencyCode)) {
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() ?
getAccountAgeCategory(getAccountAge((accountAgeWitnessOptional.get()))) :
AccountAgeWitnessService.AccountAge.LESS_ONE_MONTH;
getAccountAgeCategory(getAccountAge((accountAgeWitnessOptional.get()))) :
AccountAgeWitnessService.AccountAge.LESS_ONE_MONTH;
// TODO Fade in by date can be removed after feb 2018
// We want to fade in the limit over 2 months to avoid that all users get limited to 25% of the limit when
// We want to fade in the limit over 2 months to avoid that all users get limited to 25% of the limit when
// we deploy that feature.
final Date now = new Date();
/* final Date dez = new GregorianCalendar(2017, GregorianCalendar.DECEMBER, 1).getTime();
@ -236,38 +178,113 @@ public class AccountAgeWitnessService {
}
}
public boolean verifyAccountAgeWitness(byte[] peersAgeWitnessInputData,
AccountAgeWitness witness,
byte[] peersSalt,
PublicKey peersPublicKey,
byte[] nonce,
byte[] signatureOfNonce,
ErrorMessageHandler errorMessageHandler) {
///////////////////////////////////////////////////////////////////////////////////////////
// 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,
PubKeyRing peersPubKeyRing,
byte[] peersSignatureOfAccountHash,
byte[] nonce,
byte[] signatureOfNonce,
ErrorMessageHandler errorMessageHandler) {
// 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
if (!isTradeDateAfterReleaseDate(witness.getDate(), new GregorianCalendar(2017, GregorianCalendar.OCTOBER, 17).getTime(), errorMessageHandler))
return false;
// Check if peer's pubkey is matching the one from the witness data
if (!verifySigPubKeyHash(witness.getSigPubKeyHash(), peersPublicKey, errorMessageHandler))
return false;
final byte[] peersAccountInputDataWithSalt = Utilities.concatenateByteArrays(peersPaymentAccountPayload.getAgeWitnessInputData(), peersPaymentAccountPayload.getSalt());
byte[] hash = Utilities.concatenateByteArrays(peersAccountInputDataWithSalt, peersSignatureOfAccountHash, peersPubKeyRing.getSignaturePubKeyBytes());
final byte[] combined = ArrayUtils.addAll(peersAgeWitnessInputData, peersSalt);
byte[] hash = Hash.getSha256Ripemd160hash(combined);
// Check if the hash in the witness data matches the peer's payment account input data + salt
// Check if the hash in the witness data matches the hash derived from the data provided by the peer
if (!verifyWitnessHash(witness.getHash(), hash, errorMessageHandler))
return false;
// Check if the witness signature is correct
if (!verifySignature(peersPublicKey, hash, witness.getSignature(), errorMessageHandler))
// Check if the witness signature is correct
if (!verifyPeersTradeLimit(offer, peersPaymentAccountPayload, errorMessageHandler))
return false;
// Check if the signature of the nonce is correct
return verifySignatureOfNonce(peersPublicKey, nonce, signatureOfNonce, errorMessageHandler);
// Check if the witness signature is correct
if (!verifySignature(peersPubKeyRing.getSignaturePubKey(), peersAccountInputDataWithSalt, peersSignatureOfAccountHash, errorMessageHandler))
return false;
// Check if the signature of the nonce is correct
return verifySignatureOfNonce(peersPubKeyRing.getSignaturePubKey(), nonce, signatureOfNonce, errorMessageHandler);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Package scope verification subroutines
///////////////////////////////////////////////////////////////////////////////////////////
boolean isTradeDateAfterReleaseDate(long witnessDateAsLong, Date ageWitnessReleaseDate, ErrorMessageHandler errorMessageHandler) {
// Release date minus 1 day as tolerance for not synced clocks
Date releaseDateWithTolerance = new Date(ageWitnessReleaseDate.getTime() - TimeUnit.DAYS.toMillis(1));
@ -275,35 +292,38 @@ public class AccountAgeWitnessService {
final boolean result = witnessDate.after(releaseDateWithTolerance);
if (!result) {
final String msg = "Trade date is earlier than release date of ageWitness minus 1 day. " +
"ageWitnessReleaseDate=" + ageWitnessReleaseDate + ", witnessDate=" + witnessDate;
"ageWitnessReleaseDate=" + ageWitnessReleaseDate + ", witnessDate=" + witnessDate;
log.warn(msg);
errorMessageHandler.handleErrorMessage(msg);
}
return result;
}
boolean verifySigPubKeyHash(byte[] sigPubKeyHash,
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,
ErrorMessageHandler errorMessageHandler) {
boolean verifyWitnessHash(byte[] witnessHash,
byte[] hash,
ErrorMessageHandler errorMessageHandler) {
final boolean result = Arrays.equals(witnessHash, hash);
if (!result) {
final String msg = "witnessHash is not matching peers hash. " +
"witnessHash=" + Utilities.bytesAsHexString(witnessHash) + ", hash=" + Utilities.bytesAsHexString(hash);
"witnessHash=" + Utilities.bytesAsHexString(witnessHash) + ", hash=" + Utilities.bytesAsHexString(hash);
log.warn(msg);
errorMessageHandler.handleErrorMessage(msg);
}
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);
}
@ -323,8 +343,8 @@ public class AccountAgeWitnessService {
}
if (!result) {
final String msg = "Signature of PaymentAccountAgeWitness is not correct. " +
"peersPublicKey=" + peersPublicKey + ", data=" + Utilities.bytesAsHexString(data) +
", signature=" + Utilities.bytesAsHexString(signature);
"peersPublicKey=" + peersPublicKey + ", data=" + Utilities.bytesAsHexString(data) +
", signature=" + Utilities.bytesAsHexString(signature);
log.warn(msg);
errorMessageHandler.handleErrorMessage(msg);
}
@ -344,38 +364,8 @@ public class AccountAgeWitnessService {
}
if (!result) {
final String msg = "Signature of nonce is not correct. " +
"peersPublicKey=" + peersPublicKey + ", nonce(hex)=" + Utilities.bytesAsHexString(nonce) +
", signature=" + Utilities.bytesAsHexString(signature);
log.warn(msg);
errorMessageHandler.handleErrorMessage(msg);
}
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();
"peersPublicKey=" + peersPublicKey + ", nonce(hex)=" + Utilities.bytesAsHexString(nonce) +
", signature=" + Utilities.bytesAsHexString(signature);
log.warn(msg);
errorMessageHandler.handleErrorMessage(msg);
}

View file

@ -1,7 +1,6 @@
package io.bisq.core.payment;
import io.bisq.common.locale.TradeCurrency;
import io.bisq.common.util.Utilities;
import io.bisq.core.offer.Offer;
import io.bisq.core.payment.payload.PaymentMethod;
import javafx.collections.FXCollections;
@ -9,7 +8,6 @@ import javafx.collections.ObservableList;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkNotNull;
@ -27,8 +25,8 @@ public class PaymentAccountUtil {
public static ObservableList<PaymentAccount> getPossiblePaymentAccounts(Offer offer, Set<PaymentAccount> paymentAccounts) {
ObservableList<PaymentAccount> result = FXCollections.observableArrayList();
result.addAll(paymentAccounts.stream()
.filter(paymentAccount -> isPaymentAccountValidForOffer(offer, paymentAccount))
.collect(Collectors.toList()));
.filter(paymentAccount -> isPaymentAccountValidForOffer(offer, paymentAccount))
.collect(Collectors.toList()));
return result;
}
@ -36,16 +34,16 @@ public class PaymentAccountUtil {
public static String getInfoForMismatchingPaymentMethodLimits(Offer offer, PaymentAccount paymentAccount) {
// dont translate atm as it is not used so far in the UI just for logs
return "Payment methods have different trade limits or trade periods.\n" +
"Our local Payment method: " + paymentAccount.getPaymentMethod().toString() + "\n" +
"Payment method from offer: " + offer.getPaymentMethod().toString();
"Our local Payment method: " + paymentAccount.getPaymentMethod().toString() + "\n" +
"Payment method from offer: " + offer.getPaymentMethod().toString();
}
//TODO not tested with all combinations yet....
public static boolean isPaymentAccountValidForOffer(Offer offer, PaymentAccount paymentAccount) {
// check if we have a matching currency
Set<String> paymentAccountCurrencyCodes = paymentAccount.getTradeCurrencies().stream()
.map(TradeCurrency::getCode)
.collect(Collectors.toSet());
.map(TradeCurrency::getCode)
.collect(Collectors.toSet());
boolean matchesCurrencyCode = paymentAccountCurrencyCodes.contains(offer.getCurrencyCode());
if (!matchesCurrencyCode)
return false;
@ -54,7 +52,7 @@ public class PaymentAccountUtil {
final boolean arePaymentMethodsEqual = paymentAccount.getPaymentMethod().equals(offer.getPaymentMethod());
if (!arePaymentMethodsEqual &&
paymentAccount.getPaymentMethod().getId().equals(offer.getPaymentMethod().getId()))
paymentAccount.getPaymentMethod().getId().equals(offer.getPaymentMethod().getId()))
log.warn(getInfoForMismatchingPaymentMethodLimits(offer, paymentAccount));
if (paymentAccount instanceof CountryBasedPaymentAccount) {
@ -62,14 +60,14 @@ public class PaymentAccountUtil {
// check if we have a matching country
boolean matchesCountryCodes = offer.getAcceptedCountryCodes() != null && countryBasedPaymentAccount.getCountry() != null &&
offer.getAcceptedCountryCodes().contains(countryBasedPaymentAccount.getCountry().code);
offer.getAcceptedCountryCodes().contains(countryBasedPaymentAccount.getCountry().code);
if (!matchesCountryCodes)
return false;
if (paymentAccount instanceof SepaAccount || offer.getPaymentMethod().equals(PaymentMethod.SEPA)) {
return arePaymentMethodsEqual;
} else if (offer.getPaymentMethod().equals(PaymentMethod.SAME_BANK) ||
offer.getPaymentMethod().equals(PaymentMethod.SPECIFIC_BANKS)) {
offer.getPaymentMethod().equals(PaymentMethod.SPECIFIC_BANKS)) {
final List<String> acceptedBankIds = offer.getAcceptedBankIds();
checkNotNull(acceptedBankIds, "offer.getAcceptedBankIds() must not be null");
@ -105,30 +103,14 @@ public class PaymentAccountUtil {
public static Optional<PaymentAccount> getMostMaturePaymentAccountForOffer(Offer offer,
Set<PaymentAccount> paymentAccounts,
AccountAgeWitnessService accountAgeWitnessService) {
AccountAgeWitnessService service) {
List<PaymentAccount> list = paymentAccounts.stream()
.filter(paymentAccount -> isPaymentAccountValidForOffer(offer, paymentAccount))
.sorted((o1, o2) -> {
final Optional<AccountAgeWitness> witness1 = accountAgeWitnessService.getWitnessByPaymentAccountPayload(o1.getPaymentAccountPayload());
log.debug("witness1 isPresent={}", witness1.isPresent());
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());
list.stream().forEach(e -> log.error("getMostMaturePaymentAccountForOffer AccountName={}, witnessHashAsHex={}", e.getAccountName(), accountAgeWitnessService.getWitnessHashAsHex(e.getPaymentAccountPayload())));
.filter(paymentAccount -> isPaymentAccountValidForOffer(offer, paymentAccount))
.sorted((o1, o2) -> {
return new Long(service.getAccountAge(service.getMyWitness(o2.getPaymentAccountPayload())))
.compareTo(service.getAccountAge(service.getMyWitness(o1.getPaymentAccountPayload())));
}).collect(Collectors.toList());
list.stream().forEach(e -> log.error("getMostMaturePaymentAccountForOffer AccountName={}, witnessHashAsHex={}", e.getAccountName(), service.getMyWitnessHashAsHex(e.getPaymentAccountPayload())));
final Optional<PaymentAccount> first = list.stream().findFirst();
if (first.isPresent())
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
@Nullable
private final byte[] accountAgeWitnessSignatureOfAccountData;
@Nullable
private final byte[] accountAgeWitnessNonce;
@Nullable
private final byte[] accountAgeWitnessSignatureOfNonce;
@ -88,6 +90,7 @@ public final class PayDepositRequest extends TradeMessage {
NodeAddress mediatorNodeAddress,
String uid,
int messageVersion,
@Nullable byte[] accountAgeWitnessSignatureOfAccountData,
@Nullable byte[] accountAgeWitnessNonce,
@Nullable byte[] accountAgeWitnessSignatureOfNonce) {
super(messageVersion, tradeId);
@ -111,6 +114,7 @@ public final class PayDepositRequest extends TradeMessage {
this.arbitratorNodeAddress = arbitratorNodeAddress;
this.mediatorNodeAddress = mediatorNodeAddress;
this.uid = uid;
this.accountAgeWitnessSignatureOfAccountData = accountAgeWitnessSignatureOfAccountData;
this.accountAgeWitnessNonce = accountAgeWitnessNonce;
this.accountAgeWitnessSignatureOfNonce = accountAgeWitnessSignatureOfNonce;
}
@ -123,98 +127,101 @@ public final class PayDepositRequest extends TradeMessage {
@Override
public PB.NetworkEnvelope toProtoNetworkEnvelope() {
PB.PayDepositRequest.Builder builder = PB.PayDepositRequest.newBuilder()
.setTradeId(tradeId)
.setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setTradeAmount(tradeAmount)
.setTradePrice(tradePrice)
.setTxFee(txFee)
.setTakerFee(takerFee)
.setIsCurrencyForTakerFeeBtc(isCurrencyForTakerFeeBtc)
.addAllRawTransactionInputs(rawTransactionInputs.stream()
.map(RawTransactionInput::toProtoMessage).collect(Collectors.toList()))
.setChangeOutputValue(changeOutputValue)
.setTakerMultiSigPubKey(ByteString.copyFrom(takerMultiSigPubKey))
.setTakerPayoutAddressString(takerPayoutAddressString)
.setTakerPubKeyRing(takerPubKeyRing.toProtoMessage())
.setTakerPaymentAccountPayload((PB.PaymentAccountPayload) takerPaymentAccountPayload.toProtoMessage())
.setTakerAccountId(takerAccountId)
.setTakerFeeTxId(takerFeeTxId)
.addAllAcceptedArbitratorNodeAddresses(acceptedArbitratorNodeAddresses.stream()
.map(NodeAddress::toProtoMessage).collect(Collectors.toList()))
.addAllAcceptedMediatorNodeAddresses(acceptedMediatorNodeAddresses.stream()
.map(NodeAddress::toProtoMessage).collect(Collectors.toList()))
.setArbitratorNodeAddress(arbitratorNodeAddress.toProtoMessage())
.setMediatorNodeAddress(mediatorNodeAddress.toProtoMessage())
.setUid(uid);
.setTradeId(tradeId)
.setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setTradeAmount(tradeAmount)
.setTradePrice(tradePrice)
.setTxFee(txFee)
.setTakerFee(takerFee)
.setIsCurrencyForTakerFeeBtc(isCurrencyForTakerFeeBtc)
.addAllRawTransactionInputs(rawTransactionInputs.stream()
.map(RawTransactionInput::toProtoMessage).collect(Collectors.toList()))
.setChangeOutputValue(changeOutputValue)
.setTakerMultiSigPubKey(ByteString.copyFrom(takerMultiSigPubKey))
.setTakerPayoutAddressString(takerPayoutAddressString)
.setTakerPubKeyRing(takerPubKeyRing.toProtoMessage())
.setTakerPaymentAccountPayload((PB.PaymentAccountPayload) takerPaymentAccountPayload.toProtoMessage())
.setTakerAccountId(takerAccountId)
.setTakerFeeTxId(takerFeeTxId)
.addAllAcceptedArbitratorNodeAddresses(acceptedArbitratorNodeAddresses.stream()
.map(NodeAddress::toProtoMessage).collect(Collectors.toList()))
.addAllAcceptedMediatorNodeAddresses(acceptedMediatorNodeAddresses.stream()
.map(NodeAddress::toProtoMessage).collect(Collectors.toList()))
.setArbitratorNodeAddress(arbitratorNodeAddress.toProtoMessage())
.setMediatorNodeAddress(mediatorNodeAddress.toProtoMessage())
.setUid(uid);
Optional.ofNullable(changeOutputAddress).ifPresent(builder::setChangeOutputAddress);
Optional.ofNullable(accountAgeWitnessNonce).ifPresent(accountAgeWitnessNonce -> builder.setAccountAgeWitnessNonce(ByteString.copyFrom(accountAgeWitnessNonce)));
Optional.ofNullable(accountAgeWitnessSignatureOfNonce).ifPresent(accountAgeWitnessSignatureOfNonce -> builder.setAccountAgeWitnessSignatureOfNonce(ByteString.copyFrom(accountAgeWitnessSignatureOfNonce)));
Optional.ofNullable(accountAgeWitnessSignatureOfAccountData).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfAccountData(ByteString.copyFrom(e)));
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();
}
public static PayDepositRequest fromProto(PB.PayDepositRequest proto, CoreProtoResolver coreProtoResolver, int messageVersion) {
List<RawTransactionInput> rawTransactionInputs = proto.getRawTransactionInputsList().stream()
.map(rawTransactionInput -> new RawTransactionInput(rawTransactionInput.getIndex(),
rawTransactionInput.getParentTransaction().toByteArray(), rawTransactionInput.getValue()))
.collect(Collectors.toList());
.map(rawTransactionInput -> new RawTransactionInput(rawTransactionInput.getIndex(),
rawTransactionInput.getParentTransaction().toByteArray(), rawTransactionInput.getValue()))
.collect(Collectors.toList());
List<NodeAddress> acceptedArbitratorNodeAddresses = proto.getAcceptedArbitratorNodeAddressesList().stream()
.map(NodeAddress::fromProto).collect(Collectors.toList());
.map(NodeAddress::fromProto).collect(Collectors.toList());
List<NodeAddress> acceptedMediatorNodeAddresses = proto.getAcceptedMediatorNodeAddressesList().stream()
.map(NodeAddress::fromProto).collect(Collectors.toList());
.map(NodeAddress::fromProto).collect(Collectors.toList());
return new PayDepositRequest(proto.getTradeId(),
NodeAddress.fromProto(proto.getSenderNodeAddress()),
proto.getTradeAmount(),
proto.getTradePrice(),
proto.getTxFee(),
proto.getTakerFee(),
proto.getIsCurrencyForTakerFeeBtc(),
rawTransactionInputs,
proto.getChangeOutputValue(),
ProtoUtil.stringOrNullFromProto(proto.getChangeOutputAddress()),
proto.getTakerMultiSigPubKey().toByteArray(),
proto.getTakerPayoutAddressString(),
PubKeyRing.fromProto(proto.getTakerPubKeyRing()),
coreProtoResolver.fromProto(proto.getTakerPaymentAccountPayload()),
proto.getTakerAccountId(),
proto.getTakerFeeTxId(),
acceptedArbitratorNodeAddresses,
acceptedMediatorNodeAddresses,
NodeAddress.fromProto(proto.getArbitratorNodeAddress()),
NodeAddress.fromProto(proto.getMediatorNodeAddress()),
proto.getUid(),
messageVersion,
proto.getAccountAgeWitnessNonce().isEmpty() ? null : proto.getAccountAgeWitnessNonce().toByteArray(),
proto.getAccountAgeWitnessSignatureOfNonce().isEmpty() ? null : proto.getAccountAgeWitnessSignatureOfNonce().toByteArray());
NodeAddress.fromProto(proto.getSenderNodeAddress()),
proto.getTradeAmount(),
proto.getTradePrice(),
proto.getTxFee(),
proto.getTakerFee(),
proto.getIsCurrencyForTakerFeeBtc(),
rawTransactionInputs,
proto.getChangeOutputValue(),
ProtoUtil.stringOrNullFromProto(proto.getChangeOutputAddress()),
proto.getTakerMultiSigPubKey().toByteArray(),
proto.getTakerPayoutAddressString(),
PubKeyRing.fromProto(proto.getTakerPubKeyRing()),
coreProtoResolver.fromProto(proto.getTakerPaymentAccountPayload()),
proto.getTakerAccountId(),
proto.getTakerFeeTxId(),
acceptedArbitratorNodeAddresses,
acceptedMediatorNodeAddresses,
NodeAddress.fromProto(proto.getArbitratorNodeAddress()),
NodeAddress.fromProto(proto.getMediatorNodeAddress()),
proto.getUid(),
messageVersion,
ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfAccountData()),
ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessNonce()),
ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfNonce()));
}
@Override
public String toString() {
return "PayDepositRequest{" +
"\n senderNodeAddress=" + senderNodeAddress +
",\n tradeAmount=" + tradeAmount +
",\n tradePrice=" + tradePrice +
",\n txFee=" + txFee +
",\n takerFee=" + takerFee +
",\n isCurrencyForTakerFeeBtc=" + isCurrencyForTakerFeeBtc +
",\n rawTransactionInputs=" + rawTransactionInputs +
",\n changeOutputValue=" + changeOutputValue +
",\n changeOutputAddress='" + changeOutputAddress + '\'' +
",\n takerMultiSigPubKey=" + Utilities.bytesAsHexString(takerMultiSigPubKey) +
",\n takerPayoutAddressString='" + takerPayoutAddressString + '\'' +
",\n takerPubKeyRing=" + takerPubKeyRing +
",\n takerPaymentAccountPayload=" + takerPaymentAccountPayload +
",\n takerAccountId='" + takerAccountId + '\'' +
",\n takerFeeTxId='" + takerFeeTxId + '\'' +
",\n acceptedArbitratorNodeAddresses=" + acceptedArbitratorNodeAddresses +
",\n acceptedMediatorNodeAddresses=" + acceptedMediatorNodeAddresses +
",\n arbitratorNodeAddress=" + arbitratorNodeAddress +
",\n mediatorNodeAddress=" + mediatorNodeAddress +
",\n uid='" + uid + '\'' +
",\n accountAgeWitnessNonce=" + Utilities.bytesAsHexString(accountAgeWitnessNonce) +
",\n accountAgeWitnessSignatureOfNonce=" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfNonce) +
"\n} " + super.toString();
"\n senderNodeAddress=" + senderNodeAddress +
",\n tradeAmount=" + tradeAmount +
",\n tradePrice=" + tradePrice +
",\n txFee=" + txFee +
",\n takerFee=" + takerFee +
",\n isCurrencyForTakerFeeBtc=" + isCurrencyForTakerFeeBtc +
",\n rawTransactionInputs=" + rawTransactionInputs +
",\n changeOutputValue=" + changeOutputValue +
",\n changeOutputAddress='" + changeOutputAddress + '\'' +
",\n takerMultiSigPubKey=" + Utilities.bytesAsHexString(takerMultiSigPubKey) +
",\n takerPayoutAddressString='" + takerPayoutAddressString + '\'' +
",\n takerPubKeyRing=" + takerPubKeyRing +
",\n takerPaymentAccountPayload=" + takerPaymentAccountPayload +
",\n takerAccountId='" + takerAccountId + '\'' +
",\n takerFeeTxId='" + takerFeeTxId + '\'' +
",\n acceptedArbitratorNodeAddresses=" + acceptedArbitratorNodeAddresses +
",\n acceptedMediatorNodeAddresses=" + acceptedMediatorNodeAddresses +
",\n arbitratorNodeAddress=" + arbitratorNodeAddress +
",\n mediatorNodeAddress=" + mediatorNodeAddress +
",\n uid='" + uid + '\'' +
",\n accountAgeWitnessSignatureOfAccountData=" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfAccountData) +
",\n accountAgeWitnessNonce=" + Utilities.bytesAsHexString(accountAgeWitnessNonce) +
",\n accountAgeWitnessSignatureOfNonce=" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfNonce) +
"\n} " + super.toString();
}
}

View file

@ -19,6 +19,7 @@ package io.bisq.core.trade.messages;
import com.google.protobuf.ByteString;
import io.bisq.common.app.Version;
import io.bisq.common.proto.ProtoUtil;
import io.bisq.common.util.Utilities;
import io.bisq.core.btc.data.RawTransactionInput;
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
@Nullable
private final byte[] accountAgeWitnessSignatureOfAccountData;
@Nullable
private final byte[] accountAgeWitnessNonce;
@Nullable
private final byte[] accountAgeWitnessSignatureOfNonce;
@ -69,22 +72,24 @@ public final class PublishDepositTxRequest extends TradeMessage implements Mailb
List<RawTransactionInput> makerInputs,
NodeAddress senderNodeAddress,
String uid,
@Nullable byte[] accountAgeWitnessSignatureOfAccountData,
@Nullable byte[] accountAgeWitnessNonce,
@Nullable byte[] accountAgeWitnessSignatureOfNonce) {
this(tradeId,
makerPaymentAccountPayload,
makerAccountId,
makerMultiSigPubKey,
makerContractAsJson,
makerContractSignature,
makerPayoutAddressString,
preparedDepositTx,
makerInputs,
senderNodeAddress,
uid,
Version.getP2PMessageVersion(),
accountAgeWitnessNonce,
accountAgeWitnessSignatureOfNonce);
makerPaymentAccountPayload,
makerAccountId,
makerMultiSigPubKey,
makerContractAsJson,
makerContractSignature,
makerPayoutAddressString,
preparedDepositTx,
makerInputs,
senderNodeAddress,
uid,
Version.getP2PMessageVersion(),
accountAgeWitnessSignatureOfAccountData,
accountAgeWitnessNonce,
accountAgeWitnessSignatureOfNonce);
}
@ -104,6 +109,7 @@ public final class PublishDepositTxRequest extends TradeMessage implements Mailb
NodeAddress senderNodeAddress,
String uid,
int messageVersion,
@Nullable byte[] accountAgeWitnessSignatureOfAccountData,
@Nullable byte[] accountAgeWitnessNonce,
@Nullable byte[] accountAgeWitnessSignatureOfNonce) {
super(messageVersion, tradeId);
@ -117,6 +123,7 @@ public final class PublishDepositTxRequest extends TradeMessage implements Mailb
this.makerInputs = makerInputs;
this.senderNodeAddress = senderNodeAddress;
this.uid = uid;
this.accountAgeWitnessSignatureOfAccountData = accountAgeWitnessSignatureOfAccountData;
this.accountAgeWitnessNonce = accountAgeWitnessNonce;
this.accountAgeWitnessSignatureOfNonce = accountAgeWitnessSignatureOfNonce;
}
@ -124,63 +131,66 @@ public final class PublishDepositTxRequest extends TradeMessage implements Mailb
@Override
public PB.NetworkEnvelope toProtoNetworkEnvelope() {
final PB.PublishDepositTxRequest.Builder builder = PB.PublishDepositTxRequest.newBuilder()
.setTradeId(tradeId)
.setMakerPaymentAccountPayload((PB.PaymentAccountPayload) makerPaymentAccountPayload.toProtoMessage())
.setMakerAccountId(makerAccountId)
.setMakerMultiSigPubKey(ByteString.copyFrom(makerMultiSigPubKey))
.setMakerContractAsJson(makerContractAsJson)
.setMakerContractSignature(makerContractSignature)
.setMakerPayoutAddressString(makerPayoutAddressString)
.setPreparedDepositTx(ByteString.copyFrom(preparedDepositTx))
.addAllMakerInputs(makerInputs.stream().map(RawTransactionInput::toProtoMessage).collect(Collectors.toList()))
.setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setUid(uid);
Optional.ofNullable(accountAgeWitnessNonce).ifPresent(accountAgeWitnessNonce ->builder.setAccountAgeWitnessNonce(ByteString.copyFrom(accountAgeWitnessNonce)));
Optional.ofNullable(accountAgeWitnessSignatureOfNonce).ifPresent(accountAgeWitnessSignatureOfNonce ->builder.setAccountAgeWitnessSignatureOfNonce(ByteString.copyFrom(accountAgeWitnessSignatureOfNonce)));
.setTradeId(tradeId)
.setMakerPaymentAccountPayload((PB.PaymentAccountPayload) makerPaymentAccountPayload.toProtoMessage())
.setMakerAccountId(makerAccountId)
.setMakerMultiSigPubKey(ByteString.copyFrom(makerMultiSigPubKey))
.setMakerContractAsJson(makerContractAsJson)
.setMakerContractSignature(makerContractSignature)
.setMakerPayoutAddressString(makerPayoutAddressString)
.setPreparedDepositTx(ByteString.copyFrom(preparedDepositTx))
.addAllMakerInputs(makerInputs.stream().map(RawTransactionInput::toProtoMessage).collect(Collectors.toList()))
.setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setUid(uid);
Optional.ofNullable(accountAgeWitnessSignatureOfAccountData).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfAccountData(ByteString.copyFrom(e)));
Optional.ofNullable(accountAgeWitnessNonce).ifPresent(e -> builder.setAccountAgeWitnessNonce(ByteString.copyFrom(e)));
Optional.ofNullable(accountAgeWitnessSignatureOfNonce).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfNonce(ByteString.copyFrom(e)));
return getNetworkEnvelopeBuilder()
.setPublishDepositTxRequest(builder)
.build();
.setPublishDepositTxRequest(builder)
.build();
}
public static PublishDepositTxRequest fromProto(PB.PublishDepositTxRequest proto, CoreProtoResolver coreProtoResolver, int messageVersion) {
List<RawTransactionInput> makerInputs = proto.getMakerInputsList().stream()
.map(RawTransactionInput::fromProto)
.collect(Collectors.toList());
.map(RawTransactionInput::fromProto)
.collect(Collectors.toList());
return new PublishDepositTxRequest(proto.getTradeId(),
coreProtoResolver.fromProto(proto.getMakerPaymentAccountPayload()),
proto.getMakerAccountId(),
proto.getMakerMultiSigPubKey().toByteArray(),
proto.getMakerContractAsJson(),
proto.getMakerContractSignature(),
proto.getMakerPayoutAddressString(),
proto.getPreparedDepositTx().toByteArray(),
makerInputs,
NodeAddress.fromProto(proto.getSenderNodeAddress()),
proto.getUid(),
messageVersion,
proto.getAccountAgeWitnessNonce().isEmpty() ? null : proto.getAccountAgeWitnessNonce().toByteArray(),
proto.getAccountAgeWitnessSignatureOfNonce().isEmpty() ? null : proto.getAccountAgeWitnessSignatureOfNonce().toByteArray());
coreProtoResolver.fromProto(proto.getMakerPaymentAccountPayload()),
proto.getMakerAccountId(),
proto.getMakerMultiSigPubKey().toByteArray(),
proto.getMakerContractAsJson(),
proto.getMakerContractSignature(),
proto.getMakerPayoutAddressString(),
proto.getPreparedDepositTx().toByteArray(),
makerInputs,
NodeAddress.fromProto(proto.getSenderNodeAddress()),
proto.getUid(),
messageVersion,
ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfAccountData()),
ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessNonce()),
ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfNonce()));
}
@Override
public String toString() {
return "PublishDepositTxRequest{" +
"\n makerPaymentAccountPayload=" + makerPaymentAccountPayload +
",\n makerAccountId='" + makerAccountId + '\'' +
",\n makerMultiSigPubKey=" + Utilities.bytesAsHexString(makerMultiSigPubKey) +
",\n makerContractAsJson='" + makerContractAsJson + '\'' +
",\n makerContractSignature='" + makerContractSignature + '\'' +
",\n makerPayoutAddressString='" + makerPayoutAddressString + '\'' +
",\n preparedDepositTx=" + Utilities.bytesAsHexString(preparedDepositTx) +
",\n makerInputs=" + makerInputs +
",\n senderNodeAddress=" + senderNodeAddress +
",\n uid='" + uid + '\'' +
",\n accountAgeWitnessNonce=" + Utilities.bytesAsHexString(accountAgeWitnessNonce) +
",\n accountAgeWitnessSignatureOfNonce=" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfNonce) +
"\n} " + super.toString();
"\n makerPaymentAccountPayload=" + makerPaymentAccountPayload +
",\n makerAccountId='" + makerAccountId + '\'' +
",\n makerMultiSigPubKey=" + Utilities.bytesAsHexString(makerMultiSigPubKey) +
",\n makerContractAsJson='" + makerContractAsJson + '\'' +
",\n makerContractSignature='" + makerContractSignature + '\'' +
",\n makerPayoutAddressString='" + makerPayoutAddressString + '\'' +
",\n preparedDepositTx=" + Utilities.bytesAsHexString(preparedDepositTx) +
",\n makerInputs=" + makerInputs +
",\n senderNodeAddress=" + senderNodeAddress +
",\n uid='" + uid + '\'' +
",\n accountAgeWitnessSignatureOfAccountData=" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfAccountData) +
",\n accountAgeWitnessNonce=" + Utilities.bytesAsHexString(accountAgeWitnessNonce) +
",\n accountAgeWitnessSignatureOfNonce=" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfNonce) +
"\n} " + super.toString();
}
}

View file

@ -127,7 +127,6 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
TakerProcessPublishDepositTxRequest.class,
CheckIfPeerIsBanned.class,
TakerVerifyMakerAccount.class,
TakerVerifyOffersAccountAgeWitnessHash.class,
VerifyPeersAccountAgeWitness.class,
TakerVerifyMakerFeePayment.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.PublishAccountAgeWitness;
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.SellerProcessCounterCurrencyTransferStartedMessage;
import io.bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage;
@ -123,7 +122,6 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
TakerProcessPublishDepositTxRequest.class,
CheckIfPeerIsBanned.class,
TakerVerifyMakerAccount.class,
TakerVerifyOffersAccountAgeWitnessHash.class,
VerifyPeersAccountAgeWitness.class,
TakerVerifyMakerFeePayment.class,
TakerVerifyAndSignContract.class,

View file

@ -60,6 +60,10 @@ public final class TradingPeer implements PersistablePayload {
private long changeOutputValue;
@Nullable
private String changeOutputAddress;
// added in v 0.6
@Nullable
private byte[] accountAgeWitnessSignatureOfAccountData;
@Nullable
private byte[] accountAgeWitnessNonce;
@Nullable
@ -71,7 +75,7 @@ public final class TradingPeer implements PersistablePayload {
@Override
public Message toProtoMessage() {
final PB.TradingPeer.Builder builder = PB.TradingPeer.newBuilder()
.setChangeOutputValue(changeOutputValue);
.setChangeOutputValue(changeOutputValue);
Optional.ofNullable(accountId).ifPresent(builder::setAccountId);
Optional.ofNullable(paymentAccountPayload).ifPresent(e -> builder.setPaymentAccountPayload((PB.PaymentAccountPayload) e.toProtoMessage()));
Optional.ofNullable(payoutAddressString).ifPresent(builder::setPayoutAddressString);
@ -82,6 +86,7 @@ public final class TradingPeer implements PersistablePayload {
Optional.ofNullable(multiSigPubKey).ifPresent(e -> builder.setMultiSigPubKey(ByteString.copyFrom(e)));
Optional.ofNullable(rawTransactionInputs).ifPresent(e -> builder.addAllRawTransactionInputs(ProtoUtil.collectionToProto(e)));
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(accountAgeWitnessSignatureOfNonce).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfNonce(ByteString.copyFrom(e)));
return builder.build();
@ -102,12 +107,13 @@ public final class TradingPeer implements PersistablePayload {
tradingPeer.setPubKeyRing(proto.hasPubKeyRing() ? PubKeyRing.fromProto(proto.getPubKeyRing()) : null);
tradingPeer.setMultiSigPubKey(ProtoUtil.byteArrayOrNullFromProto(proto.getMultiSigPubKey()));
List<RawTransactionInput> rawTransactionInputs = proto.getRawTransactionInputsList().isEmpty() ?
null :
proto.getRawTransactionInputsList().stream()
.map(RawTransactionInput::fromProto)
.collect(Collectors.toList());
null :
proto.getRawTransactionInputsList().stream()
.map(RawTransactionInput::fromProto)
.collect(Collectors.toList());
tradingPeer.setRawTransactionInputs(rawTransactionInputs);
tradingPeer.setChangeOutputAddress(ProtoUtil.stringOrNullFromProto(proto.getChangeOutputAddress()));
tradingPeer.setAccountAgeWitnessNonce(ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfAccountData()));
tradingPeer.setAccountAgeWitnessNonce(ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessNonce()));
tradingPeer.setAccountAgeWitnessSignatureOfNonce(ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfNonce()));
return tradingPeer;

View file

@ -32,7 +32,7 @@ public class PublishAccountAgeWitness extends TradeTask {
protected void run() {
try {
runInterceptHook();
processModel.getAccountAgeWitnessService().publishAccountAgeWitness(processModel.getPaymentAccountPayload(trade));
processModel.getAccountAgeWitnessService().publishMyAccountAgeWitness(processModel.getPaymentAccountPayload(trade));
complete();
} catch (Throwable 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.taskrunner.TaskRunner;
import io.bisq.core.offer.Offer;
import io.bisq.core.payment.AccountAgeWitness;
import io.bisq.core.payment.AccountAgeWitnessService;
import io.bisq.core.payment.payload.PaymentAccountPayload;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.TradingPeer;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
@ -45,49 +47,46 @@ public class VerifyPeersAccountAgeWitness extends TradeTask {
runInterceptHook();
final AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService();
final PaymentAccountPayload peersPaymentAccountPayload = checkNotNull(processModel.getTradingPeer().getPaymentAccountPayload(),
"Peers peersPaymentAccountPayload must not be null");
final String[] errorMsg1 = new String[1];
boolean result = accountAgeWitnessService.verifyTradeLimit(trade.getOffer(), peersPaymentAccountPayload, errorMessage -> errorMsg1[0] = errorMessage);
if (result) {
byte[] nonce = processModel.getTradingPeer().getAccountAgeWitnessNonce();
byte[] signatureOfNonce = processModel.getTradingPeer().getAccountAgeWitnessSignatureOfNonce();
Optional<AccountAgeWitness> witnessOptional = accountAgeWitnessService.getWitnessByPaymentAccountPayload(peersPaymentAccountPayload);
if (witnessOptional.isPresent() && nonce != null && signatureOfNonce != null) {
AccountAgeWitness witness = witnessOptional.get();
final PubKeyRing pubKeyRing = processModel.getTradingPeer().getPubKeyRing();
checkNotNull(pubKeyRing, "processModel.getTradingPeer().getPubKeyRing() must not be null");
final String[] errorMsg2 = new String[1];
result = accountAgeWitnessService.verifyAccountAgeWitness(peersPaymentAccountPayload.getAgeWitnessInputData(),
witness,
peersPaymentAccountPayload.getSalt(),
pubKeyRing.getSignaturePubKey(),
nonce,
signatureOfNonce,
errorMessage -> errorMsg2[0] = errorMessage);
if (result)
complete();
else
failed(errorMsg2[0]);
} else {
String msg = !witnessOptional.isPresent() ?
"Peers AccountAgeWitness is not found." :
"Peer seems to uses a pre v0.6 application which does not support sending of account age witness verification nonce and signature.";
msg += "\nTrade ID=" + trade.getId();
if (new Date().after(new GregorianCalendar(2018, GregorianCalendar.FEBRUARY, 1).getTime())) {
msg = "The account age witness verification failed.\nReason: " + msg;
log.error(msg);
failed(msg);
} else {
log.warn(msg + "\nWe tolerate that until 1. of Feb. 2018");
complete();
}
}
final TradingPeer tradingPeer = processModel.getTradingPeer();
final PaymentAccountPayload peersPaymentAccountPayload = checkNotNull(tradingPeer.getPaymentAccountPayload(),
"Peers peersPaymentAccountPayload must not be null");
final PubKeyRing peersPubKeyRing = checkNotNull(tradingPeer.getPubKeyRing(), "peersPubKeyRing must not be null");
final Offer offer = trade.getOffer();
final Optional<String> accountAgeWitnessHashAsHex = offer.getAccountAgeWitnessHashAsHex();
Optional<AccountAgeWitness> witnessOptional = accountAgeWitnessHashAsHex.isPresent() ?
accountAgeWitnessService.getPeersWitnessByHashAsHex(accountAgeWitnessHashAsHex.get())
: Optional.<AccountAgeWitness>empty();
byte[] nonce = tradingPeer.getAccountAgeWitnessNonce();
byte[] signatureOfNonce = tradingPeer.getAccountAgeWitnessSignatureOfNonce();
if (witnessOptional.isPresent() && nonce != null && signatureOfNonce != null) {
AccountAgeWitness witness = witnessOptional.get();
final String[] errorMsg = new String[1];
byte[] peersSignatureOfAccountHash = tradingPeer.getAccountAgeWitnessSignatureOfAccountData();
boolean result = accountAgeWitnessService.verifyPeersAccountAgeWitness(offer,
peersPaymentAccountPayload,
witness,
peersPubKeyRing,
peersSignatureOfAccountHash,
nonce,
signatureOfNonce,
errorMessage -> errorMsg[0] = errorMessage);
if (result)
complete();
else
failed(errorMsg[0]);
} else {
String msg = "The offer verification failed.\nReason: " + errorMsg1[0];
log.error(msg);
failed(msg);
String msg = !witnessOptional.isPresent() ?
"Peers AccountAgeWitness is not found." :
"Peer seems to uses a pre v0.6 application which does not support sending of account age witness verification nonce and signature.";
msg += "\nTrade ID=" + trade.getId();
if (new Date().after(new GregorianCalendar(2018, GregorianCalendar.FEBRUARY, 1).getTime())) {
msg = "The account age witness verification failed.\nReason: " + msg;
log.error(msg);
failed(msg);
} else {
log.warn(msg + "\nWe tolerate offers without account age witness until first of Feb. 2018");
complete();
}
}
} catch (Throwable t) {
failed(t);

View file

@ -66,13 +66,14 @@ public class MakerProcessPayDepositRequest extends TradeTask {
if (payDepositRequest.getAcceptedArbitratorNodeAddresses().isEmpty())
failed("acceptedArbitratorNames must not be empty");
processModel.getTradingPeer().setAccountAgeWitnessSignatureOfAccountData(payDepositRequest.getAccountAgeWitnessSignatureOfAccountData());
final byte[] accountAgeWitnessNonce = payDepositRequest.getAccountAgeWitnessNonce();
processModel.getTradingPeer().setAccountAgeWitnessNonce(accountAgeWitnessNonce);
processModel.getTradingPeer().setAccountAgeWitnessSignatureOfNonce(payDepositRequest.getAccountAgeWitnessSignatureOfNonce());
// 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.
checkArgument(Arrays.equals(accountAgeWitnessNonce, trade.getOffer().getId().getBytes()));
trade.setArbitratorNodeAddress(checkNotNull(payDepositRequest.getArbitratorNodeAddress()));
trade.setMediatorNodeAddress(checkNotNull(payDepositRequest.getMediatorNodeAddress()));
@ -98,4 +99,4 @@ public class MakerProcessPayDepositRequest extends TradeTask {
failed(t);
}
}
}
}

View file

@ -21,6 +21,7 @@ import io.bisq.common.crypto.Sig;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.btc.AddressEntry;
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.messages.PublishDepositTxRequest;
import io.bisq.core.trade.protocol.tasks.TradeTask;
@ -32,6 +33,7 @@ import java.util.Optional;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j
public class MakerSendPublishDepositTxRequest extends TradeTask {
@ -52,60 +54,66 @@ public class MakerSendPublishDepositTxRequest extends TradeTask {
AddressEntry makerPayoutAddressEntry = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.TRADE_PAYOUT);
byte[] makerMultiSigPubKey = processModel.getMyMultiSigPubKey();
checkArgument(Arrays.equals(makerMultiSigPubKey,
addressEntryOptional.get().getPubKey()),
"makerMultiSigPubKey from AddressEntry must match the one from the trade data. trade id =" + id);
addressEntryOptional.get().getPubKey()),
"makerMultiSigPubKey from AddressEntry must match the one from the trade data. trade id =" + id);
final byte[] preparedDepositTx = processModel.getPreparedDepositTx();
// 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.
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[] accountAgeWitnessSignatureOfNonce = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), accountAgeWitnessNonce);
PublishDepositTxRequest message = new PublishDepositTxRequest(
processModel.getOfferId(),
processModel.getPaymentAccountPayload(trade),
processModel.getAccountId(),
makerMultiSigPubKey,
trade.getContractAsJson(),
trade.getMakerContractSignature(),
makerPayoutAddressEntry.getAddressString(),
preparedDepositTx,
processModel.getRawTransactionInputs(),
processModel.getMyNodeAddress(),
UUID.randomUUID().toString(),
accountAgeWitnessNonce,
accountAgeWitnessSignatureOfNonce);
processModel.getOfferId(),
paymentAccountPayload,
processModel.getAccountId(),
makerMultiSigPubKey,
trade.getContractAsJson(),
trade.getMakerContractSignature(),
makerPayoutAddressEntry.getAddressString(),
preparedDepositTx,
processModel.getRawTransactionInputs(),
processModel.getMyNodeAddress(),
UUID.randomUUID().toString(),
accountAgeWitnessSignatureOfAccountData,
accountAgeWitnessNonce,
accountAgeWitnessSignatureOfNonce);
trade.setState(Trade.State.MAKER_SENT_PUBLISH_DEPOSIT_TX_REQUEST);
processModel.getP2PService().sendEncryptedMailboxMessage(
trade.getTradingPeerNodeAddress(),
processModel.getTradingPeer().getPubKeyRing(),
message,
new SendMailboxMessageListener() {
@Override
public void onArrived() {
log.info("Message arrived at peer. tradeId={}", id);
trade.setState(Trade.State.MAKER_SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST);
complete();
}
@Override
public void onStoredInMailbox() {
log.info("Message stored in mailbox. tradeId={}", id);
trade.setState(Trade.State.MAKER_STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST);
complete();
}
@Override
public void onFault(String errorMessage) {
log.error("sendEncryptedMailboxMessage failed. message=" + message);
trade.setState(Trade.State.MAKER_SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST);
appendToErrorMessage("Sending message failed: message=" + message + "\nerrorMessage=" + errorMessage);
failed(errorMessage);
}
trade.getTradingPeerNodeAddress(),
processModel.getTradingPeer().getPubKeyRing(),
message,
new SendMailboxMessageListener() {
@Override
public void onArrived() {
log.info("Message arrived at peer. tradeId={}", id);
trade.setState(Trade.State.MAKER_SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST);
complete();
}
@Override
public void onStoredInMailbox() {
log.info("Message stored in mailbox. tradeId={}", id);
trade.setState(Trade.State.MAKER_STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST);
complete();
}
@Override
public void onFault(String errorMessage) {
log.error("sendEncryptedMailboxMessage failed. message=" + message);
trade.setState(Trade.State.MAKER_SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST);
appendToErrorMessage("Sending message failed: message=" + message + "\nerrorMessage=" + errorMessage);
failed(errorMessage);
}
}
);
} catch (Throwable t) {

View file

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

View file

@ -56,10 +56,11 @@ public class TakerProcessPublishDepositTxRequest extends TradeTask {
final byte[] preparedDepositTx = publishDepositTxRequest.getPreparedDepositTx();
processModel.setPreparedDepositTx(checkNotNull(preparedDepositTx));
processModel.getTradingPeer().setAccountAgeWitnessSignatureOfAccountData(publishDepositTxRequest.getAccountAgeWitnessSignatureOfAccountData());
final byte[] accountAgeWitnessNonce = publishDepositTxRequest.getAccountAgeWitnessNonce();
processModel.getTradingPeer().setAccountAgeWitnessNonce(accountAgeWitnessNonce);
processModel.getTradingPeer().setAccountAgeWitnessSignatureOfNonce(publishDepositTxRequest.getAccountAgeWitnessSignatureOfNonce());
// 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.
checkArgument(Arrays.equals(accountAgeWitnessNonce, preparedDepositTx));
@ -74,4 +75,4 @@ public class TakerProcessPublishDepositTxRequest extends TradeTask {
failed(t);
}
}
}
}

View file

@ -22,6 +22,7 @@ import io.bisq.common.crypto.Sig;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.btc.AddressEntry;
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.messages.PayDepositRequest;
import io.bisq.core.trade.protocol.tasks.TradeTask;
@ -61,7 +62,7 @@ public class TakerSendPayDepositRequest extends TradeTask {
String id = processModel.getOffer().getId();
checkArgument(!walletService.getAddressEntry(id, AddressEntry.Context.MULTI_SIG).isPresent(),
"addressEntry must not be set here.");
"addressEntry must not be set here.");
AddressEntry addressEntry = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.MULTI_SIG);
byte[] takerMultiSigPubKey = addressEntry.getPubKey();
processModel.setMyMultiSigPubKey(takerMultiSigPubKey);
@ -70,55 +71,59 @@ public class TakerSendPayDepositRequest extends TradeTask {
String takerPayoutAddressString = takerPayoutAddressEntry.getAddressString();
final String offerId = processModel.getOfferId();
// 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.
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[] accountAgeWitnessSignatureOfNonce = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), accountAgeWitnessNonce);
PayDepositRequest message = new PayDepositRequest(
offerId,
processModel.getMyNodeAddress(),
trade.getTradeAmount().value,
trade.getTradePrice().getValue(),
trade.getTxFee().getValue(),
trade.getTakerFee().getValue(),
trade.isCurrencyForTakerFeeBtc(),
processModel.getRawTransactionInputs(),
processModel.getChangeOutputValue(),
processModel.getChangeOutputAddress(),
takerMultiSigPubKey,
takerPayoutAddressString,
processModel.getPubKeyRing(),
processModel.getPaymentAccountPayload(trade),
processModel.getAccountId(),
trade.getTakerFeeTxId(),
new ArrayList<>(acceptedArbitratorAddresses),
new ArrayList<>(acceptedMediatorAddresses),
trade.getArbitratorNodeAddress(),
trade.getMediatorNodeAddress(),
UUID.randomUUID().toString(),
Version.getP2PMessageVersion(),
accountAgeWitnessNonce,
accountAgeWitnessSignatureOfNonce);
offerId,
processModel.getMyNodeAddress(),
trade.getTradeAmount().value,
trade.getTradePrice().getValue(),
trade.getTxFee().getValue(),
trade.getTakerFee().getValue(),
trade.isCurrencyForTakerFeeBtc(),
processModel.getRawTransactionInputs(),
processModel.getChangeOutputValue(),
processModel.getChangeOutputAddress(),
takerMultiSigPubKey,
takerPayoutAddressString,
processModel.getPubKeyRing(),
paymentAccountPayload,
processModel.getAccountId(),
trade.getTakerFeeTxId(),
new ArrayList<>(acceptedArbitratorAddresses),
new ArrayList<>(acceptedMediatorAddresses),
trade.getArbitratorNodeAddress(),
trade.getMediatorNodeAddress(),
UUID.randomUUID().toString(),
Version.getP2PMessageVersion(),
accountAgeWitnessSignatureOfAccountData,
accountAgeWitnessNonce,
accountAgeWitnessSignatureOfNonce);
processModel.getP2PService().sendEncryptedDirectMessage(
trade.getTradingPeerNodeAddress(),
processModel.getTradingPeer().getPubKeyRing(),
message,
new SendDirectMessageListener() {
@Override
public void onArrived() {
log.debug("Message arrived at peer. tradeId={}, message{}", id, message);
complete();
}
@Override
public void onFault() {
appendToErrorMessage("Sending message failed: message=" + message + "\nerrorMessage=" + errorMessage);
failed();
}
trade.getTradingPeerNodeAddress(),
processModel.getTradingPeer().getPubKeyRing(),
message,
new SendDirectMessageListener() {
@Override
public void onArrived() {
log.debug("Message arrived at peer. tradeId={}, message{}", id, message);
complete();
}
@Override
public void onFault() {
appendToErrorMessage("Sending message failed: message=" + message + "\nerrorMessage=" + errorMessage);
failed();
}
}
);
} catch (Throwable t) {
failed(t);

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 -> {}));
}
@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
public void testVerifySignature() throws CryptoException {
byte[] ageWitnessInputData = "test".getBytes(Charset.forName("UTF-8"));

View file

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

View file

@ -151,10 +151,10 @@ public abstract class PaymentMethodForm {
CurrencyUtil.getDefaultTradeCurrency();
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",
getTimeText(hours),
formatter.formatCoinWithCode(Coin.valueOf(accountAgeWitnessService.getTradeLimit(paymentAccount, tradeCurrency.getCode()))),
formatter.formatCoinWithCode(Coin.valueOf(accountAgeWitnessService.getMyTradeLimit(paymentAccount, tradeCurrency.getCode()))),
formatter.formatAccountAge(accountAge)));
if (isAddAccountScreen) {
@ -165,7 +165,7 @@ public abstract class PaymentMethodForm {
try {
// test if input is hex
Utilities.decodeFromHex(newValue);
paymentAccount.setSaltAsHex(newValue);
} catch (Throwable t) {
new Popup().warning(Res.get("payment.error.noHexSalt")).show();

View file

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

View file

@ -60,7 +60,7 @@ class AltCoinAccountsDataModel extends ActivatableDataModel {
OpenOfferManager openOfferManager,
TradeManager tradeManager,
AccountAgeWitnessService accountAgeWitnessService,
Stage stage,
Stage stage,
PersistenceProtoResolver persistenceProtoResolver) {
this.user = user;
this.preferences = preferences;
@ -115,7 +115,7 @@ class AltCoinAccountsDataModel extends ActivatableDataModel {
});
}
accountAgeWitnessService.publishAccountAgeWitness(paymentAccount.getPaymentAccountPayload());
accountAgeWitnessService.publishMyAccountAgeWitness(paymentAccount.getPaymentAccountPayload());
}
public boolean onDeleteAccount(PaymentAccount paymentAccount) {

View file

@ -115,8 +115,8 @@ class FiatAccountsDataModel extends ActivatableDataModel {
preferences.addCryptoCurrency((CryptoCurrency) tradeCurrency);
});
}
accountAgeWitnessService.publishAccountAgeWitness(paymentAccount.getPaymentAccountPayload());
accountAgeWitnessService.publishMyAccountAgeWitness(paymentAccount.getPaymentAccountPayload());
}
public boolean onDeleteAccount(PaymentAccount paymentAccount) {

View file

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

View file

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

View file

@ -401,10 +401,10 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
} else if (isInsufficientTradeLimit) {
final Optional<PaymentAccount> account = model.getMostMaturePaymentAccountForOffer(offer);
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<>()
.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(offer.getMinAmount())))
.show();

View file

@ -464,7 +464,7 @@ class OfferBookViewModel extends ActivatableViewModel {
boolean isInsufficientTradeLimit(Offer 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;
log.debug("isInsufficientTradeLimit accountOptional={}, myTradeLimit={}, offerMinAmount={}, ",
accountOptional.isPresent() ? accountOptional.get().getAccountName() : "null",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -147,7 +147,7 @@ public class SellerStep3View extends TradeStepView {
TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, ++gridRow, 3, Res.get("portfolio.pending.step3_seller.confirmPaymentReceipt"), Layout.GROUP_DISTANCE);
TextFieldWithCopyIcon field = addLabelTextFieldWithCopyIcon(gridPane, gridRow, Res.get("portfolio.pending.step3_seller.amountToReceive"),
model.getFiatVolume(), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
model.getFiatVolume(), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
field.setCopyWithoutCurrencyPostFix(true);
String myPaymentDetails = "";
@ -219,8 +219,8 @@ public class SellerStep3View extends TradeStepView {
protected String getWarningText() {
setWarningHeadline();
String substitute = model.isBlockChainMethod() ?
Res.get("portfolio.pending.step3_seller.warn.part1a", model.dataModel.getCurrencyCode()) :
Res.get("portfolio.pending.step3_seller.warn.part1b");
Res.get("portfolio.pending.step3_seller.warn.part1a", model.dataModel.getCurrencyCode()) :
Res.get("portfolio.pending.step3_seller.warn.part1b");
return Res.get("portfolio.pending.step3_seller.warn.part2", substitute, model.getDateForOpenDispute());
@ -266,13 +266,13 @@ public class SellerStep3View extends TradeStepView {
}
message += Res.get("portfolio.pending.step3_seller.onPaymentReceived.note");
new Popup<>()
.headLine(Res.get("portfolio.pending.step3_seller.onPaymentReceived.confirm.headline"))
.confirmation(message)
.width(700)
.actionButtonText(Res.get("portfolio.pending.step3_seller.onPaymentReceived.confirm.yes"))
.onAction(this::confirmPaymentReceived)
.closeButtonText(Res.get("shared.cancel"))
.show();
.headLine(Res.get("portfolio.pending.step3_seller.onPaymentReceived.confirm.headline"))
.confirmation(message)
.width(700)
.actionButtonText(Res.get("portfolio.pending.step3_seller.onPaymentReceived.confirm.yes"))
.onAction(this::confirmPaymentReceived)
.closeButtonText(Res.get("shared.cancel"))
.show();
} else {
confirmPaymentReceived();
}
@ -316,8 +316,8 @@ public class SellerStep3View extends TradeStepView {
if (!DevEnv.DEV_MODE && DontShowAgainLookup.showAgain(key)) {
DontShowAgainLookup.dontShowAgain(key, true);
new Popup<>().headLine(Res.get("popup.attention.forTradeWithId", id))
.attention(message)
.show();
.attention(message)
.show();
}
}
@ -350,9 +350,9 @@ public class SellerStep3View extends TradeStepView {
else if (paymentAccountPayload instanceof SepaAccountPayload)
return Optional.of(((SepaAccountPayload) paymentAccountPayload).getHolderName());
else
return Optional.empty();
return Optional.<String>empty();
} else {
return Optional.empty();
return Optional.<String>empty();
}
}
}

View file

@ -103,7 +103,7 @@ public class Connection implements MessageListener {
private OutputStream protoOutputStream;
// 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 PeerType peerType;
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<String, GetDataRequestHandler> getDataRequestHandlers = new HashMap<>();
private Optional<NodeAddress> nodeAddressOfPreliminaryDataRequest = Optional.empty();
private Optional<NodeAddress> nodeAddressOfPreliminaryDataRequest = Optional.<NodeAddress>empty();
private Timer retryTimer;
private boolean dataUpdateRequested;
private boolean stopped;