Add LegacyBurningMan to UI

It is not included in BurningManCandidate candidate map as that would complicate things.
We only want to show it in the UI for informational purposes.

Signed-off-by: HenrikJannsen <boilingfrog@gmx.com>
This commit is contained in:
HenrikJannsen 2022-11-10 19:20:09 -05:00
parent 883b0542a1
commit d4646cc19f
No known key found for this signature in database
GPG key ID: 02AA2BAE387C8307
6 changed files with 179 additions and 46 deletions

View file

@ -33,18 +33,18 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@Getter
@EqualsAndHashCode
public final class BurningManCandidate {
public class BurningManCandidate {
private final List<CompensationModel> compensationModels = new ArrayList<>();
private long accumulatedCompensationAmount;
private long accumulatedDecayedCompensationAmount;
private double compensationShare; // Share of accumulated decayed compensation amounts in relation to total issued amounts
private Optional<String> mostRecentAddress = Optional.empty();
protected Optional<String> mostRecentAddress = Optional.empty();
private final List<BurnOutputModel> burnOutputModels = new ArrayList<>();
private long accumulatedBurnAmount;
private long accumulatedDecayedBurnAmount;
private double burnAmountShare; // Share of accumulated decayed burn amounts in relation to total burned amounts
private double cappedBurnAmountShare; // Capped burnAmountShare. Cannot be larger than boostedCompensationShare
protected double burnAmountShare; // Share of accumulated decayed burn amounts in relation to total burned amounts
protected double cappedBurnAmountShare; // Capped burnAmountShare. Cannot be larger than boostedCompensationShare
BurningManCandidate() {
}

View file

@ -18,14 +18,21 @@
package bisq.core.dao.burningman;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.CyclesInDaoStateService;
import bisq.core.dao.governance.proposal.MyProposalListService;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.BaseTx;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.blockchain.Tx;
import bisq.core.dao.state.model.blockchain.TxOutput;
import bisq.core.dao.state.model.governance.CompensationProposal;
import bisq.core.dao.state.model.governance.Proposal;
import bisq.network.p2p.storage.P2PDataStorage;
import bisq.common.app.DevEnv;
import bisq.common.util.Hex;
import bisq.common.util.Tuple2;
import javax.inject.Inject;
@ -48,8 +55,10 @@ public class BurningManPresentationService implements DaoStateListener {
// Burn target gets increased by that amount to give more flexibility.
// Burn target is calculated from reimbursements + estimated BTC fees - burned amounts.
static final long BURN_TARGET_BOOST_AMOUNT = DevEnv.isDevTesting() ? 1000000 : 10000000;
public static final String LEGACY_BURNING_MAN_NAME = "Legacy Burningman";
private final DaoStateService daoStateService;
private final CyclesInDaoStateService cyclesInDaoStateService;
private final MyProposalListService myProposalListService;
private final BsqWalletService bsqWalletService;
private final BurningManService burningManService;
@ -63,14 +72,18 @@ public class BurningManPresentationService implements DaoStateListener {
private Set<String> myCompensationRequestNames = null;
@SuppressWarnings("OptionalAssignedToNull")
private Optional<Set<String>> myGenesisOutputNames = null;
private Optional<LegacyBurningMan> legacyBurningMan = Optional.empty();
private final Map<P2PDataStorage.ByteArray, Set<TxOutput>> proofOfBurnOpReturnTxOutputByHash = new HashMap<>();
@Inject
public BurningManPresentationService(DaoStateService daoStateService,
CyclesInDaoStateService cyclesInDaoStateService,
MyProposalListService myProposalListService,
BsqWalletService bsqWalletService,
BurningManService burningManService,
BurnTargetService burnTargetService) {
this.daoStateService = daoStateService;
this.cyclesInDaoStateService = cyclesInDaoStateService;
this.myProposalListService = myProposalListService;
this.bsqWalletService = bsqWalletService;
this.burningManService = burningManService;
@ -96,6 +109,8 @@ public class BurningManPresentationService implements DaoStateListener {
burnTarget = Optional.empty();
myCompensationRequestNames = null;
averageDistributionPerCycle = Optional.empty();
legacyBurningMan = Optional.empty();
proofOfBurnOpReturnTxOutputByHash.clear();
}
@ -223,4 +238,49 @@ public class BurningManPresentationService implements DaoStateListener {
burningManCandidatesByName.putAll(burningManService.getBurningManCandidatesByName(currentChainHeight));
return burningManCandidatesByName;
}
public LegacyBurningMan getLegacyBurningMan() {
if (legacyBurningMan.isPresent()) {
return legacyBurningMan.get();
}
if (proofOfBurnOpReturnTxOutputByHash.isEmpty()) {
proofOfBurnOpReturnTxOutputByHash.putAll(burningManService.getProofOfBurnOpReturnTxOutputByHash(currentChainHeight));
}
// We do not add the legacy burningman to the list but keep it as class field only to avoid that it
// interferes with usage of the burningManCandidatesByName map.
LegacyBurningMan legacyBurningMan = new LegacyBurningMan(burningManService.getLegacyBurningManAddress(currentChainHeight));
// Those are the hashes used by legacy BM for burning
Set<String> hashes = Set.of("1701e47e5d8030f444c182b5e243871ebbaeadb5e82f",
"1701293c488822f98e70e047012f46f5f1647f37deb7",
"1701721206fe6b40777763de1c741f4fd2706d94775d");
proofOfBurnOpReturnTxOutputByHash.values().stream()
.flatMap(txOutputs -> txOutputs.stream()
.filter(txOutput -> {
String hash = Hex.encode(txOutput.getOpReturnData());
return hashes.stream().anyMatch(e -> e.equals(hash));
}))
.forEach(burnOutput -> {
int burnOutputHeight = burnOutput.getBlockHeight();
Optional<Tx> optionalTx = daoStateService.getTx(burnOutput.getTxId());
long burnOutputAmount = optionalTx.map(Tx::getBurntBsq).orElse(0L);
long date = optionalTx.map(BaseTx::getTime).orElse(0L);
int cycleIndex = cyclesInDaoStateService.getCycleIndexAtChainHeight(burnOutputHeight);
legacyBurningMan.addBurnOutputModel(new BurnOutputModel(burnOutputAmount,
burnOutputAmount,
burnOutputHeight,
burnOutput.getTxId(),
date,
cycleIndex));
});
// Set remaining share if the sum of all capped shares does not reach 100%.
double burnAmountShareOfOthers = getBurningManCandidatesByName().values().stream()
.mapToDouble(BurningManCandidate::getCappedBurnAmountShare)
.sum();
legacyBurningMan.applyBurnAmountShare(1 - burnAmountShareOfOthers);
this.legacyBurningMan = Optional.of(legacyBurningMan);
return legacyBurningMan;
}
}

View file

@ -178,7 +178,6 @@ class BurningManService {
return burningManCandidatesByName;
}
String getLegacyBurningManAddress(int chainHeight) {
return daoStateService.getParamValue(Param.RECIPIENT_BTC_ADDRESS, chainHeight);
}
@ -188,7 +187,7 @@ class BurningManService {
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private Map<P2PDataStorage.ByteArray, Set<TxOutput>> getProofOfBurnOpReturnTxOutputByHash(int chainHeight) {
Map<P2PDataStorage.ByteArray, Set<TxOutput>> getProofOfBurnOpReturnTxOutputByHash(int chainHeight) {
Map<P2PDataStorage.ByteArray, Set<TxOutput>> map = new HashMap<>();
daoStateService.getProofOfBurnOpReturnTxOutputs().stream()
.filter(txOutput -> txOutput.getBlockHeight() <= chainHeight)

View file

@ -0,0 +1,43 @@
/*
* 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 bisq.core.dao.burningman;
import java.util.Optional;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Getter
@EqualsAndHashCode(callSuper = true)
public final class LegacyBurningMan extends BurningManCandidate {
LegacyBurningMan(String address) {
mostRecentAddress = Optional.of(address);
}
void applyBurnAmountShare(double burnAmountShare) {
this.burnAmountShare = burnAmountShare;
this.cappedBurnAmountShare = burnAmountShare;
}
@Override
public void calculateShares(double totalDecayedCompensationAmounts, double totalDecayedBurnAmounts) {
// do nothing
}
}

View file

@ -19,6 +19,7 @@ package bisq.desktop.main.dao.burnbsq.burningmen;
import bisq.core.dao.burningman.BurningManCandidate;
import bisq.core.dao.burningman.BurningManPresentationService;
import bisq.core.dao.burningman.LegacyBurningMan;
import bisq.core.locale.Res;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.BsqFormatter;
@ -32,9 +33,9 @@ import lombok.Getter;
@EqualsAndHashCode
class BurningmenListItem {
private final BurningManCandidate burningManCandidate;
private final String name, address, burnAmountShareAsString, cappedBurnAmountShareAsString, compensationShareAsString,
accumulatedDecayedBurnAmountAsBsq, burnTargetAsBsq, maxBurnTargetAsBsq, accumulatedBurnAmountAsBsq,
accumulatedDecayedCompensationAmountAsBsq, accumulatedCompensationAmountAsBsq, expectedRevenueAsBsq;
private final String name, address, cappedBurnAmountShareAsString, compensationShareAsString,
accumulatedDecayedBurnAmountAsBsq, burnTargetAsBsq, accumulatedBurnAmountAsBsq,
accumulatedDecayedCompensationAmountAsBsq, accumulatedCompensationAmountAsBsq, expectedRevenueAsBsq, numIssuancesAsString;
private final long burnTarget, maxBurnTarget, accumulatedDecayedBurnAmount, accumulatedBurnAmount,
accumulatedDecayedCompensationAmount, accumulatedCompensationAmount, expectedRevenue;
private final int numBurnOutputs, numIssuances;
@ -49,31 +50,65 @@ class BurningmenListItem {
this.name = name;
address = burningManCandidate.getMostRecentAddress().orElse(Res.get("shared.na"));
// Burn
Tuple2<Long, Long> burnTargetTuple = burningManPresentationService.getCandidateBurnTarget(burningManCandidate);
burnTarget = burnTargetTuple.first;
burnTargetAsBsq = bsqFormatter.formatCoin(burnTarget);
maxBurnTarget = burnTargetTuple.second;
maxBurnTargetAsBsq = bsqFormatter.formatCoin(maxBurnTarget);
accumulatedBurnAmount = burningManCandidate.getAccumulatedBurnAmount();
accumulatedBurnAmountAsBsq = bsqFormatter.formatCoinWithCode(accumulatedBurnAmount);
accumulatedDecayedBurnAmount = burningManCandidate.getAccumulatedDecayedBurnAmount();
accumulatedDecayedBurnAmountAsBsq = bsqFormatter.formatCoinWithCode(accumulatedDecayedBurnAmount);
burnAmountShare = burningManCandidate.getBurnAmountShare();
burnAmountShareAsString = FormattingUtils.formatToPercentWithSymbol(burnAmountShare);
cappedBurnAmountShare = burningManCandidate.getCappedBurnAmountShare();
cappedBurnAmountShareAsString = FormattingUtils.formatToPercentWithSymbol(cappedBurnAmountShare);
expectedRevenue = burningManPresentationService.getExpectedRevenue(burningManCandidate);
expectedRevenueAsBsq = bsqFormatter.formatCoinWithCode(expectedRevenue);
numBurnOutputs = burningManCandidate.getBurnOutputModels().size();
if (burningManCandidate instanceof LegacyBurningMan) {
// Burn
burnTarget = 0;
burnTargetAsBsq = "";
maxBurnTarget = 0;
accumulatedBurnAmount = burningManCandidate.getAccumulatedBurnAmount();
accumulatedBurnAmountAsBsq = bsqFormatter.formatCoinWithCode(accumulatedBurnAmount);
accumulatedDecayedBurnAmount = 0;
accumulatedDecayedBurnAmountAsBsq = "";
// Issuance
accumulatedCompensationAmount = burningManCandidate.getAccumulatedCompensationAmount();
accumulatedCompensationAmountAsBsq = bsqFormatter.formatCoinWithCode(accumulatedCompensationAmount);
accumulatedDecayedCompensationAmount = burningManCandidate.getAccumulatedDecayedCompensationAmount();
accumulatedDecayedCompensationAmountAsBsq = bsqFormatter.formatCoinWithCode(accumulatedDecayedCompensationAmount);
compensationShare = burningManCandidate.getCompensationShare();
compensationShareAsString = FormattingUtils.formatToPercentWithSymbol(compensationShare);
numIssuances = burningManCandidate.getCompensationModels().size();
burnAmountShare = burningManCandidate.getBurnAmountShare();
cappedBurnAmountShareAsString = FormattingUtils.formatToPercentWithSymbol(burnAmountShare);
cappedBurnAmountShare = burningManCandidate.getCappedBurnAmountShare();
expectedRevenue = burningManPresentationService.getExpectedRevenue(burningManCandidate);
expectedRevenueAsBsq = bsqFormatter.formatCoinWithCode(expectedRevenue);
numBurnOutputs = burningManCandidate.getBurnOutputModels().size();
// There is no issuance for legacy BM
accumulatedCompensationAmount = 0;
accumulatedCompensationAmountAsBsq = "";
accumulatedDecayedCompensationAmount = 0;
accumulatedDecayedCompensationAmountAsBsq = "";
compensationShare = 0;
compensationShareAsString = "";
numIssuances = 0;
numIssuancesAsString = "";
} else {
// Burn
Tuple2<Long, Long> burnTargetTuple = burningManPresentationService.getCandidateBurnTarget(burningManCandidate);
burnTarget = burnTargetTuple.first;
maxBurnTarget = burnTargetTuple.second;
burnTargetAsBsq = Res.get("dao.burningmen.burnTarget.fromTo", bsqFormatter.formatCoin(burnTarget), bsqFormatter.formatCoin(maxBurnTarget));
accumulatedBurnAmount = burningManCandidate.getAccumulatedBurnAmount();
accumulatedBurnAmountAsBsq = bsqFormatter.formatCoinWithCode(accumulatedBurnAmount);
accumulatedDecayedBurnAmount = burningManCandidate.getAccumulatedDecayedBurnAmount();
accumulatedDecayedBurnAmountAsBsq = bsqFormatter.formatCoinWithCode(accumulatedDecayedBurnAmount);
burnAmountShare = burningManCandidate.getBurnAmountShare();
cappedBurnAmountShare = burningManCandidate.getCappedBurnAmountShare();
if (burnAmountShare != cappedBurnAmountShare) {
cappedBurnAmountShareAsString = Res.get("dao.burningmen.table.burnAmountShare.capped",
FormattingUtils.formatToPercentWithSymbol(cappedBurnAmountShare),
FormattingUtils.formatToPercentWithSymbol(burnAmountShare));
} else {
cappedBurnAmountShareAsString = FormattingUtils.formatToPercentWithSymbol(cappedBurnAmountShare);
}
expectedRevenue = burningManPresentationService.getExpectedRevenue(burningManCandidate);
expectedRevenueAsBsq = bsqFormatter.formatCoinWithCode(expectedRevenue);
numBurnOutputs = burningManCandidate.getBurnOutputModels().size();
// Issuance
accumulatedCompensationAmount = burningManCandidate.getAccumulatedCompensationAmount();
accumulatedCompensationAmountAsBsq = bsqFormatter.formatCoinWithCode(accumulatedCompensationAmount);
accumulatedDecayedCompensationAmount = burningManCandidate.getAccumulatedDecayedCompensationAmount();
accumulatedDecayedCompensationAmountAsBsq = bsqFormatter.formatCoinWithCode(accumulatedDecayedCompensationAmount);
compensationShare = burningManCandidate.getCompensationShare();
compensationShareAsString = FormattingUtils.formatToPercentWithSymbol(compensationShare);
numIssuances = burningManCandidate.getCompensationModels().size();
numIssuancesAsString = String.valueOf(numIssuances);
}
}
}

View file

@ -444,6 +444,10 @@ public class BurningmenView extends ActivatableView<ScrollPane, Void> implements
burningmenObservableList.setAll(burningManPresentationService.getBurningManCandidatesByName().entrySet().stream()
.map(entry -> new BurningmenListItem(burningManPresentationService, entry.getKey(), entry.getValue(), bsqFormatter))
.collect(Collectors.toList()));
burningmenObservableList.add(new BurningmenListItem(burningManPresentationService,
BurningManPresentationService.LEGACY_BURNING_MAN_NAME,
burningManPresentationService.getLegacyBurningMan(),
bsqFormatter));
reimbursementObservableList.setAll(burningManPresentationService.getReimbursements().stream()
.map(reimbursementModel -> new ReimbursementListItem(reimbursementModel, bsqFormatter))
.collect(Collectors.toList()));
@ -564,9 +568,7 @@ public class BurningmenView extends ActivatableView<ScrollPane, Void> implements
public void updateItem(final BurningmenListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
setText(Res.get("dao.burningmen.burnTarget.fromTo",
item.getBurnTargetAsBsq(),
item.getMaxBurnTargetAsBsq()));
setText(item.getBurnTargetAsBsq());
} else
setText("");
}
@ -614,13 +616,7 @@ public class BurningmenView extends ActivatableView<ScrollPane, Void> implements
public void updateItem(final BurningmenListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
if (item.getBurnAmountShare() != item.getCappedBurnAmountShare()) {
setText(Res.get("dao.burningmen.table.burnAmountShare.capped",
item.getCappedBurnAmountShareAsString(),
item.getBurnAmountShareAsString()));
} else {
setText(item.getBurnAmountShareAsString());
}
setText(item.getCappedBurnAmountShareAsString());
} else
setText("");
}
@ -657,7 +653,7 @@ public class BurningmenView extends ActivatableView<ScrollPane, Void> implements
column.setSortType(TableColumn.SortType.DESCENDING);
column = new AutoTooltipTableColumn<>(Res.get("dao.burningmen.table.burnAmount"));
column.setMinWidth(110);
column.setMinWidth(130);
column.getStyleClass().add("last-column");
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(new Callback<>() {
@ -791,7 +787,7 @@ public class BurningmenView extends ActivatableView<ScrollPane, Void> implements
public void updateItem(final BurningmenListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
setText(String.valueOf(item.getNumIssuances()));
setText(item.getNumIssuancesAsString());
} else
setText("");
}