support additional info on all offers

This commit is contained in:
woodser 2025-01-25 12:41:20 -05:00
parent a6af1550a4
commit 6c6c6e2dd5
45 changed files with 367 additions and 204 deletions

View file

@ -425,6 +425,7 @@ public class CoreApi {
String paymentAccountId,
boolean isPrivateOffer,
boolean buyerAsTakerWithoutDeposit,
String extraInfo,
Consumer<Offer> resultHandler,
ErrorMessageHandler errorMessageHandler) {
coreOffersService.postOffer(currencyCode,
@ -440,6 +441,7 @@ public class CoreApi {
paymentAccountId,
isPrivateOffer,
buyerAsTakerWithoutDeposit,
extraInfo,
resultHandler,
errorMessageHandler);
}
@ -455,7 +457,8 @@ public class CoreApi {
double securityDepositPct,
PaymentAccount paymentAccount,
boolean isPrivateOffer,
boolean buyerAsTakerWithoutDeposit) {
boolean buyerAsTakerWithoutDeposit,
String extraInfo) {
return coreOffersService.editOffer(offerId,
currencyCode,
direction,
@ -467,7 +470,8 @@ public class CoreApi {
securityDepositPct,
paymentAccount,
isPrivateOffer,
buyerAsTakerWithoutDeposit);
buyerAsTakerWithoutDeposit,
extraInfo);
}
public void cancelOffer(String id, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {

View file

@ -178,6 +178,7 @@ public class CoreOffersService {
String paymentAccountId,
boolean isPrivateOffer,
boolean buyerAsTakerWithoutDeposit,
String extraInfo,
Consumer<Offer> resultHandler,
ErrorMessageHandler errorMessageHandler) {
coreWalletsService.verifyWalletsAreAvailable();
@ -204,7 +205,8 @@ public class CoreOffersService {
securityDepositPct,
paymentAccount,
isPrivateOffer,
buyerAsTakerWithoutDeposit);
buyerAsTakerWithoutDeposit,
extraInfo);
verifyPaymentAccountIsValidForNewOffer(offer, paymentAccount);
@ -230,7 +232,8 @@ public class CoreOffersService {
double securityDepositPct,
PaymentAccount paymentAccount,
boolean isPrivateOffer,
boolean buyerAsTakerWithoutDeposit) {
boolean buyerAsTakerWithoutDeposit,
String extraInfo) {
return createOfferService.createAndGetOffer(offerId,
direction,
currencyCode.toUpperCase(),
@ -242,7 +245,8 @@ public class CoreOffersService {
securityDepositPct,
paymentAccount,
isPrivateOffer,
buyerAsTakerWithoutDeposit);
buyerAsTakerWithoutDeposit,
extraInfo);
}
void cancelOffer(String id, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {

View file

@ -80,6 +80,7 @@ public class OfferInfo implements Payload {
private final long splitOutputTxFee;
private final boolean isPrivateOffer;
private final String challenge;
private final String extraInfo;
public OfferInfo(OfferInfoBuilder builder) {
this.id = builder.getId();
@ -115,6 +116,7 @@ public class OfferInfo implements Payload {
this.splitOutputTxFee = builder.getSplitOutputTxFee();
this.isPrivateOffer = builder.isPrivateOffer();
this.challenge = builder.getChallenge();
this.extraInfo = builder.getExtraInfo();
}
public static OfferInfo toOfferInfo(Offer offer) {
@ -184,7 +186,8 @@ public class OfferInfo implements Payload {
.withProtocolVersion(offer.getOfferPayload().getProtocolVersion())
.withArbitratorSigner(offer.getOfferPayload().getArbitratorSigner() == null ? null : offer.getOfferPayload().getArbitratorSigner().getFullAddress())
.withIsPrivateOffer(offer.isPrivateOffer())
.withChallenge(offer.getChallenge());
.withChallenge(offer.getChallenge())
.withExtraInfo(offer.getCombinedExtraInfo());
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -227,6 +230,7 @@ public class OfferInfo implements Payload {
Optional.ofNullable(arbitratorSigner).ifPresent(builder::setArbitratorSigner);
Optional.ofNullable(splitOutputTxHash).ifPresent(builder::setSplitOutputTxHash);
Optional.ofNullable(challenge).ifPresent(builder::setChallenge);
Optional.ofNullable(extraInfo).ifPresent(builder::setExtraInfo);
return builder.build();
}
@ -266,6 +270,7 @@ public class OfferInfo implements Payload {
.withSplitOutputTxFee(proto.getSplitOutputTxFee())
.withIsPrivateOffer(proto.getIsPrivateOffer())
.withChallenge(proto.getChallenge())
.withExtraInfo(proto.getExtraInfo())
.build();
}
}

View file

@ -65,6 +65,7 @@ public final class OfferInfoBuilder {
private long splitOutputTxFee;
private boolean isPrivateOffer;
private String challenge;
private String extraInfo;
public OfferInfoBuilder withId(String id) {
this.id = id;
@ -246,6 +247,11 @@ public final class OfferInfoBuilder {
return this;
}
public OfferInfoBuilder withExtraInfo(String extraInfo) {
this.extraInfo = extraInfo;
return this;
}
public OfferInfo build() {
return new OfferInfo(this);
}

View file

@ -103,7 +103,8 @@ public class CreateOfferService {
double securityDepositPct,
PaymentAccount paymentAccount,
boolean isPrivateOffer,
boolean buyerAsTakerWithoutDeposit) {
boolean buyerAsTakerWithoutDeposit,
String extraInfo) {
log.info("create and get offer with offerId={}, " +
"currencyCode={}, " +
"direction={}, " +
@ -114,7 +115,8 @@ public class CreateOfferService {
"minAmount={}, " +
"securityDepositPct={}, " +
"isPrivateOffer={}, " +
"buyerAsTakerWithoutDeposit={}",
"buyerAsTakerWithoutDeposit={}, " +
"extraInfo={}",
offerId,
currencyCode,
direction,
@ -125,7 +127,8 @@ public class CreateOfferService {
minAmount,
securityDepositPct,
isPrivateOffer,
buyerAsTakerWithoutDeposit);
buyerAsTakerWithoutDeposit,
extraInfo);
// verify buyer as taker security deposit
boolean isBuyerMaker = offerUtil.isBuyOffer(direction);
@ -225,7 +228,8 @@ public class CreateOfferService {
Version.TRADE_PROTOCOL_VERSION,
null,
null,
null);
null,
extraInfo);
Offer offer = new Offer(offerPayload);
offer.setPriceFeedService(priceFeedService);
offer.setChallenge(challenge);

View file

@ -421,7 +421,23 @@ public class Offer implements NetworkPayload, PersistablePayload {
return "";
}
public String getExtraInfo() {
public String getCombinedExtraInfo() {
StringBuilder sb = new StringBuilder();
if (getOfferExtraInfo() != null && !getOfferExtraInfo().isEmpty()) {
sb.append(getOfferExtraInfo());
}
if (getPaymentAccountExtraInfo() != null && !getPaymentAccountExtraInfo().isEmpty()) {
if (sb.length() > 0) sb.append("\n\n");
sb.append(getPaymentAccountExtraInfo());
}
return sb.toString();
}
public String getOfferExtraInfo() {
return offerPayload.getExtraInfo();
}
public String getPaymentAccountExtraInfo() {
if (getExtraDataMap() != null && getExtraDataMap().containsKey(OfferPayload.F2F_EXTRA_INFO))
return getExtraDataMap().get(OfferPayload.F2F_EXTRA_INFO);
else if (getExtraDataMap() != null && getExtraDataMap().containsKey(OfferPayload.PAY_BY_MAIL_EXTRA_INFO))

View file

@ -158,6 +158,8 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
private final boolean isPrivateOffer;
@Nullable
private final String challengeHash;
@Nullable
private final String extraInfo;
///////////////////////////////////////////////////////////////////////////////////////////
@ -201,7 +203,8 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
int protocolVersion,
@Nullable NodeAddress arbitratorSigner,
@Nullable byte[] arbitratorSignature,
@Nullable List<String> reserveTxKeyImages) {
@Nullable List<String> reserveTxKeyImages,
@Nullable String extraInfo) {
this.id = id;
this.date = date;
this.ownerNodeAddress = ownerNodeAddress;
@ -240,6 +243,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
this.upperClosePrice = upperClosePrice;
this.isPrivateOffer = isPrivateOffer;
this.challengeHash = challengeHash;
this.extraInfo = extraInfo;
}
public byte[] getHash() {
@ -290,7 +294,8 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
protocolVersion,
arbitratorSigner,
null,
reserveTxKeyImages
reserveTxKeyImages,
null
);
return signee.getHash();
@ -387,6 +392,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
Optional.ofNullable(arbitratorSigner).ifPresent(e -> builder.setArbitratorSigner(arbitratorSigner.toProtoMessage()));
Optional.ofNullable(arbitratorSignature).ifPresent(e -> builder.setArbitratorSignature(ByteString.copyFrom(e)));
Optional.ofNullable(reserveTxKeyImages).ifPresent(builder::addAllReserveTxKeyImages);
Optional.ofNullable(extraInfo).ifPresent(builder::setExtraInfo);
return protobuf.StoragePayload.newBuilder().setOfferPayload(builder).build();
}
@ -398,7 +404,6 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
null : new ArrayList<>(proto.getAcceptedCountryCodesList());
List<String> reserveTxKeyImages = proto.getReserveTxKeyImagesList().isEmpty() ?
null : new ArrayList<>(proto.getReserveTxKeyImagesList());
String challengeHash = ProtoUtil.stringOrNullFromProto(proto.getChallengeHash());
Map<String, String> extraDataMapMap = CollectionUtils.isEmpty(proto.getExtraDataMap()) ?
null : proto.getExtraDataMap();
@ -434,12 +439,13 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
proto.getLowerClosePrice(),
proto.getUpperClosePrice(),
proto.getIsPrivateOffer(),
challengeHash,
ProtoUtil.stringOrNullFromProto(proto.getChallengeHash()),
extraDataMapMap,
proto.getProtocolVersion(),
proto.hasArbitratorSigner() ? NodeAddress.fromProto(proto.getArbitratorSigner()) : null,
ProtoUtil.byteArrayOrNullFromProto(proto.getArbitratorSignature()),
reserveTxKeyImages);
reserveTxKeyImages,
ProtoUtil.stringOrNullFromProto(proto.getExtraInfo()));
}
@Override
@ -481,14 +487,15 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
",\r\n lowerClosePrice=" + lowerClosePrice +
",\r\n upperClosePrice=" + upperClosePrice +
",\r\n isPrivateOffer=" + isPrivateOffer +
",\r\n challengeHash='" + challengeHash + '\'' +
",\r\n challengeHash='" + challengeHash +
",\r\n arbitratorSigner=" + arbitratorSigner +
",\r\n arbitratorSignature=" + Utilities.bytesAsHexString(arbitratorSignature) +
",\r\n extraInfo='" + extraInfo +
"\r\n} ";
}
// For backward compatibility we need to ensure same order for json fields as with 1.7.5. and earlier versions.
// The json is used for the hash in the contract and change of oder would cause a different hash and
// The json is used for the hash in the contract and change of order would cause a different hash and
// therefore a failure during trade.
public static class JsonSerializer implements com.google.gson.JsonSerializer<OfferPayload> {
@Override
@ -525,6 +532,8 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
object.add("protocolVersion", context.serialize(offerPayload.getProtocolVersion()));
object.add("arbitratorSigner", context.serialize(offerPayload.getArbitratorSigner()));
object.add("arbitratorSignature", context.serialize(offerPayload.getArbitratorSignature()));
object.add("extraInfo", context.serialize(offerPayload.getExtraInfo()));
// reserveTxKeyImages and challengeHash are purposely excluded because they are not relevant to existing trades and would break existing contracts
return object;
}
}

View file

@ -1788,7 +1788,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
protocolVersion,
originalOfferPayload.getArbitratorSigner(),
originalOfferPayload.getArbitratorSignature(),
originalOfferPayload.getReserveTxKeyImages());
originalOfferPayload.getReserveTxKeyImages(),
originalOfferPayload.getExtraInfo());
// Save states from original data to use for the updated
Offer.State originalOfferState = originalOffer.getState();

View file

@ -93,7 +93,7 @@ public final class F2FAccount extends CountryBasedPaymentAccount {
if (field.getId() == PaymentAccountFormField.FieldId.TRADE_CURRENCIES) field.setComponent(PaymentAccountFormField.Component.SELECT_ONE);
if (field.getId() == PaymentAccountFormField.FieldId.CITY) field.setLabel(Res.get("payment.f2f.city"));
if (field.getId() == PaymentAccountFormField.FieldId.CONTACT) field.setLabel(Res.get("payment.f2f.contact"));
if (field.getId() == PaymentAccountFormField.FieldId.EXTRA_INFO) field.setLabel(Res.get("payment.shared.extraInfo.prompt"));
if (field.getId() == PaymentAccountFormField.FieldId.EXTRA_INFO) field.setLabel(Res.get("payment.shared.extraInfo.prompt.paymentAccount"));
return field;
}
}