Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup signed witness #4744

Merged
merged 6 commits into from
Nov 5, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@
@Slf4j
public class AccountAgeWitnessService {
private static final Date RELEASE = Utilities.getUTCDate(2017, GregorianCalendar.NOVEMBER, 11);
public static final Date FULL_ACTIVATION = Utilities.getUTCDate(2018, GregorianCalendar.FEBRUARY, 15);
private static final long SAFE_ACCOUNT_AGE_DATE = Utilities.getUTCDate(2019, GregorianCalendar.MARCH, 1).getTime();

public enum AccountAge {
Expand Down Expand Up @@ -208,10 +207,11 @@ private void republishAllFiatAccounts() {
user.getPaymentAccounts().stream()
.filter(e -> !(e instanceof AssetAccount))
.forEach(e -> {
// We delay with a random interval of 20-60 sec to ensure to be better connected and don't stress the
// P2P network with publishing all at once at startup time.
// We delay with a random interval of 20-60 sec to ensure to be better connected and don't
// stress the P2P network with publishing all at once at startup time.
final int delayInSec = 20 + new Random().nextInt(40);
UserThread.runAfter(() -> p2PService.addPersistableNetworkPayload(getMyWitness(e.getPaymentAccountPayload()), true), delayInSec);
UserThread.runAfter(() -> p2PService.addPersistableNetworkPayload(getMyWitness(
e.getPaymentAccountPayload()), true), delayInSec);
});
}

Expand All @@ -238,7 +238,8 @@ public byte[] getPeerAccountAgeWitnessHash(Trade trade) {
}

byte[] getAccountInputDataWithSalt(PaymentAccountPayload paymentAccountPayload) {
return Utilities.concatenateByteArrays(paymentAccountPayload.getAgeWitnessInputData(), paymentAccountPayload.getSalt());
return Utilities.concatenateByteArrays(paymentAccountPayload.getAgeWitnessInputData(),
paymentAccountPayload.getSalt());
}

@VisibleForTesting
Expand Down Expand Up @@ -281,7 +282,8 @@ private Optional<AccountAgeWitness> getWitnessByHash(byte[] hash) {
if (!containsKey)
log.debug("hash not found in accountAgeWitnessMap");

return accountAgeWitnessMap.containsKey(hashAsByteArray) ? Optional.of(accountAgeWitnessMap.get(hashAsByteArray)) : Optional.empty();
return accountAgeWitnessMap.containsKey(hashAsByteArray) ?
Optional.of(accountAgeWitnessMap.get(hashAsByteArray)) : Optional.empty();
}

private Optional<AccountAgeWitness> getWitnessByHashAsHex(String hashAsHex) {
Expand Down Expand Up @@ -359,65 +361,57 @@ private AccountAge getAccountAgeCategory(long accountAge) {
}
}

// Checks trade limit based on time since signing of AccountAgeWitness
// Get trade limit based on a time schedule
// Buying of BTC with a payment method that has chargeback risk will use a low trade limit schedule
// All selling and all other fiat payment methods use the normal trade limit schedule
// Non fiat always has max limit
// Account types that can get signed will use time since signing, other methods use time since account age creation
// when measuring account age
private long getTradeLimit(Coin maxTradeLimit,
String currencyCode,
AccountAgeWitness accountAgeWitness,
AccountAge accountAgeCategory,
OfferPayload.Direction direction,
PaymentMethod paymentMethod) {
if (CurrencyUtil.isFiatCurrency(currencyCode)) {
double factor;
boolean isRisky = PaymentMethod.hasChargebackRisk(paymentMethod, currencyCode);
if (!isRisky || direction == OfferPayload.Direction.SELL) {
// Get age of witness rather than time since signing for non risky payment methods and for selling
accountAgeCategory = getAccountAgeCategory(getAccountAge(accountAgeWitness, new Date()));
}
long limit = OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value;
if (direction == OfferPayload.Direction.BUY && isRisky) {
// Used only for bying of BTC with risky payment methods
switch (accountAgeCategory) {
case TWO_MONTHS_OR_MORE:
factor = 1;
break;
case ONE_TO_TWO_MONTHS:
factor = 0.5;
break;
case LESS_ONE_MONTH:
case UNVERIFIED:
default:
factor = 0;
}
} else {
// Used by non risky payment methods and for selling BTC with risky methods
switch (accountAgeCategory) {
case TWO_MONTHS_OR_MORE:
factor = 1;
break;
case ONE_TO_TWO_MONTHS:
factor = 0.5;
break;
case LESS_ONE_MONTH:
case UNVERIFIED:
factor = 0.25;
break;
default:
factor = 0;
}
}
if (factor > 0) {
limit = MathUtils.roundDoubleToLong((double) maxTradeLimit.value * factor);
}

log.debug("accountAgeCategory={}, limit={}, factor={}, accountAgeWitnessHash={}",
accountAgeCategory,
Coin.valueOf(limit).toFriendlyString(),
factor,
Utilities.bytesAsHexString(accountAgeWitness.getHash()));
return limit;
} else {
if (!CurrencyUtil.isFiatCurrency(currencyCode)) {
return maxTradeLimit.value;
}

long limit = OfferRestrictions.TOLERATED_SMALL_TRADE_AMOUNT.value;
var factor = PaymentMethod.hasChargebackRisk(paymentMethod, currencyCode) ?
signedTypeFactor(accountAgeCategory, direction) : normalFactor();
if (factor > 0) {
limit = MathUtils.roundDoubleToLong((double) maxTradeLimit.value * factor);
}

log.debug("limit={}, factor={}, accountAgeWitnessHash={}",
Coin.valueOf(limit).toFriendlyString(),
factor,
Utilities.bytesAsHexString(accountAgeWitness.getHash()));
return limit;
}

private double signedTypeFactor(AccountAge accountAgeCategory,
OfferPayload.Direction direction) {
return direction == OfferPayload.Direction.BUY ? signedBuyFactor(accountAgeCategory) :
normalFactor();
}

private double signedBuyFactor(AccountAge accountAgeCategory) {
switch (accountAgeCategory) {
case TWO_MONTHS_OR_MORE:
return 1;
case ONE_TO_TWO_MONTHS:
return 0.5;
case LESS_ONE_MONTH:
case UNVERIFIED:
default:
}
return 0;
}

private double normalFactor() {
return 1;
}

///////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -444,7 +438,8 @@ private boolean hasTradeLimitException(AccountAgeWitness accountAgeWitness) {
///////////////////////////////////////////////////////////////////////////////////////////

public AccountAgeWitness getMyWitness(PaymentAccountPayload paymentAccountPayload) {
final Optional<AccountAgeWitness> accountAgeWitnessOptional = findWitness(paymentAccountPayload, keyRing.getPubKeyRing());
final Optional<AccountAgeWitness> accountAgeWitnessOptional =
findWitness(paymentAccountPayload, keyRing.getPubKeyRing());
return accountAgeWitnessOptional.orElseGet(() -> getNewWitness(paymentAccountPayload, keyRing.getPubKeyRing()));
}

Expand All @@ -460,8 +455,7 @@ public long getMyAccountAge(PaymentAccountPayload paymentAccountPayload) {
return getAccountAge(getMyWitness(paymentAccountPayload), new Date());
}

public long getMyTradeLimit(PaymentAccount paymentAccount, String currencyCode, OfferPayload.Direction
direction) {
public long getMyTradeLimit(PaymentAccount paymentAccount, String currencyCode, OfferPayload.Direction direction) {
if (paymentAccount == null)
return 0;

Expand Down Expand Up @@ -492,7 +486,8 @@ public boolean verifyAccountAgeWitness(Trade trade,
byte[] nonce,
byte[] signature,
ErrorMessageHandler errorMessageHandler) {
final Optional<AccountAgeWitness> accountAgeWitnessOptional = findWitness(peersPaymentAccountPayload, peersPubKeyRing);
final Optional<AccountAgeWitness> accountAgeWitnessOptional =
findWitness(peersPaymentAccountPayload, peersPubKeyRing);
// If we don't find a stored witness data we create a new dummy object which makes is easier to reuse the
// below validation methods. This peersWitness object is not used beside for validation. Some of the
// validation calls are pointless in the case we create a new Witness ourselves but the verifyPeersTradeLimit
Expand All @@ -513,8 +508,10 @@ public boolean verifyAccountAgeWitness(Trade trade,
if (!verifyPeersCurrentDate(peersCurrentDate, errorMessageHandler))
return false;

final byte[] peersAccountInputDataWithSalt = Utilities.concatenateByteArrays(peersPaymentAccountPayload.getAgeWitnessInputData(), peersPaymentAccountPayload.getSalt());
byte[] hash = Hash.getSha256Ripemd160hash(Utilities.concatenateByteArrays(peersAccountInputDataWithSalt, peersPubKeyRing.getSignaturePubKeyBytes()));
final byte[] peersAccountInputDataWithSalt = Utilities.concatenateByteArrays(
peersPaymentAccountPayload.getAgeWitnessInputData(), peersPaymentAccountPayload.getSalt());
byte[] hash = Hash.getSha256Ripemd160hash(Utilities.concatenateByteArrays(peersAccountInputDataWithSalt,
peersPubKeyRing.getSignaturePubKeyBytes()));

// Check if the hash in the witness data matches the hash derived from the data provided by the peer
final byte[] peersWitnessHash = peersWitness.getHash();
Expand Down Expand Up @@ -591,7 +588,8 @@ private boolean verifyPeersTradeLimit(Offer offer,
ErrorMessageHandler errorMessageHandler) {
checkNotNull(offer);
final String currencyCode = offer.getCurrencyCode();
final Coin defaultMaxTradeLimit = PaymentMethod.getPaymentMethodById(offer.getOfferPayload().getPaymentMethodId()).getMaxTradeLimitAsCoin(currencyCode);
final Coin defaultMaxTradeLimit = PaymentMethod.getPaymentMethodById(
offer.getOfferPayload().getPaymentMethodId()).getMaxTradeLimitAsCoin(currencyCode);
long peersCurrentTradeLimit = defaultMaxTradeLimit.value;
if (!hasTradeLimitException(peersWitness)) {
final long accountSignAge = getWitnessSignAge(peersWitness, peersCurrentDate);
Expand Down Expand Up @@ -654,8 +652,8 @@ public String arbitratorSignOrphanWitness(AccountAgeWitness accountAgeWitness,
.findAny()
.orElse(null);
checkNotNull(signedWitness);
return signedWitnessService.signAndPublishAccountAgeWitness(accountAgeWitness, key, signedWitness.getWitnessOwnerPubKey(),
time);
return signedWitnessService.signAndPublishAccountAgeWitness(accountAgeWitness, key,
signedWitness.getWitnessOwnerPubKey(), time);
}

public String arbitratorSignOrphanPubKey(ECKey key,
Expand All @@ -676,7 +674,8 @@ public Optional<SignedWitness> traderSignAndPublishPeersAccountAgeWitness(Trade
Coin tradeAmount = trade.getTradeAmount();
checkNotNull(trade.getProcessModel().getTradingPeer().getPubKeyRing(), "Peer must have a keyring");
PublicKey peersPubKey = trade.getProcessModel().getTradingPeer().getPubKeyRing().getSignaturePubKey();
checkNotNull(peersWitness, "Not able to find peers witness, unable to sign for trade {}", trade.toString());
checkNotNull(peersWitness, "Not able to find peers witness, unable to sign for trade {}",
trade.toString());
checkNotNull(tradeAmount, "Trade amount must not be null");
checkNotNull(peersPubKey, "Peers pub key must not be null");

Expand Down Expand Up @@ -717,7 +716,8 @@ private boolean isNotFiltered(Dispute dispute) {
filterManager.isPaymentMethodBanned(
PaymentMethod.getPaymentMethodById(dispute.getContract().getPaymentMethodId())) ||
filterManager.arePeersPaymentAccountDataBanned(dispute.getContract().getBuyerPaymentAccountPayload()) ||
filterManager.arePeersPaymentAccountDataBanned(dispute.getContract().getSellerPaymentAccountPayload()) ||
filterManager.arePeersPaymentAccountDataBanned(
dispute.getContract().getSellerPaymentAccountPayload()) ||
filterManager.isWitnessSignerPubKeyBanned(
Utils.HEX.encode(dispute.getContract().getBuyerPubKeyRing().getSignaturePubKeyBytes())) ||
filterManager.isWitnessSignerPubKeyBanned(
Expand Down