Cleanup bond domain

This commit is contained in:
Manfred Karrer 2018-11-06 14:58:12 -05:00
parent edc1a35b15
commit bee2d1ebd3
No known key found for this signature in database
GPG Key ID: 401250966A6B2C46
41 changed files with 418 additions and 450 deletions

View File

@ -25,16 +25,16 @@ import bisq.core.dao.governance.ballot.BallotListService;
import bisq.core.dao.governance.blindvote.BlindVoteConsensus;
import bisq.core.dao.governance.blindvote.MyBlindVoteListService;
import bisq.core.dao.governance.bond.Bond;
import bisq.core.dao.governance.bond.lockup.LockupService;
import bisq.core.dao.governance.bond.lockup.LockupType;
import bisq.core.dao.governance.bond.lockup.LockupReason;
import bisq.core.dao.governance.bond.lockup.LockupTxService;
import bisq.core.dao.governance.bond.reputation.BondedReputation;
import bisq.core.dao.governance.bond.reputation.BondedReputationService;
import bisq.core.dao.governance.bond.reputation.BondedReputationRepository;
import bisq.core.dao.governance.bond.reputation.MyBondedReputation;
import bisq.core.dao.governance.bond.reputation.MyBondedReputationService;
import bisq.core.dao.governance.bond.reputation.MyBondedReputationRepository;
import bisq.core.dao.governance.bond.reputation.MyReputationListService;
import bisq.core.dao.governance.bond.role.BondedRole;
import bisq.core.dao.governance.bond.role.BondedRolesService;
import bisq.core.dao.governance.bond.unlock.UnlockService;
import bisq.core.dao.governance.bond.role.BondedRolesRepository;
import bisq.core.dao.governance.bond.unlock.UnlockTxService;
import bisq.core.dao.governance.myvote.MyVote;
import bisq.core.dao.governance.myvote.MyVoteListService;
import bisq.core.dao.governance.param.Param;
@ -119,12 +119,12 @@ public class DaoFacade implements DaoSetupService {
private final RoleProposalFactory roleProposalFactory;
private final GenericProposalFactory genericProposalFactory;
private final RemoveAssetProposalFactory removeAssetProposalFactory;
private final BondedRolesService bondedRolesService;
private final BondedReputationService bondedReputationService;
private final BondedRolesRepository bondedRolesRepository;
private final BondedReputationRepository bondedReputationRepository;
private final MyReputationListService myReputationListService;
private final MyBondedReputationService myBondedReputationService;
private final LockupService lockupService;
private final UnlockService unlockService;
private final MyBondedReputationRepository myBondedReputationRepository;
private final LockupTxService lockupTxService;
private final UnlockTxService unlockTxService;
private final DaoStateStorageService daoStateStorageService;
private final ObjectProperty<DaoPhase.Phase> phaseProperty = new SimpleObjectProperty<>(DaoPhase.Phase.UNDEFINED);
@ -145,12 +145,12 @@ public class DaoFacade implements DaoSetupService {
RoleProposalFactory roleProposalFactory,
GenericProposalFactory genericProposalFactory,
RemoveAssetProposalFactory removeAssetProposalFactory,
BondedRolesService bondedRolesService,
BondedReputationService bondedReputationService,
BondedRolesRepository bondedRolesRepository,
BondedReputationRepository bondedReputationRepository,
MyReputationListService myReputationListService,
MyBondedReputationService myBondedReputationService,
LockupService lockupService,
UnlockService unlockService,
MyBondedReputationRepository myBondedReputationRepository,
LockupTxService lockupTxService,
UnlockTxService unlockTxService,
DaoStateStorageService daoStateStorageService) {
this.proposalListPresentation = proposalListPresentation;
this.ballotListService = ballotListService;
@ -167,12 +167,12 @@ public class DaoFacade implements DaoSetupService {
this.roleProposalFactory = roleProposalFactory;
this.genericProposalFactory = genericProposalFactory;
this.removeAssetProposalFactory = removeAssetProposalFactory;
this.bondedRolesService = bondedRolesService;
this.bondedReputationService = bondedReputationService;
this.bondedRolesRepository = bondedRolesRepository;
this.bondedReputationRepository = bondedReputationRepository;
this.myReputationListService = myReputationListService;
this.myBondedReputationService = myBondedReputationService;
this.lockupService = lockupService;
this.unlockService = unlockService;
this.myBondedReputationRepository = myBondedReputationRepository;
this.lockupTxService = lockupTxService;
this.unlockTxService = unlockTxService;
this.daoStateStorageService = daoStateStorageService;
}
@ -292,7 +292,7 @@ public class DaoFacade implements DaoSetupService {
}
public List<BondedRole> getBondedRoles() {
return bondedRolesService.getBonds();
return bondedRolesRepository.getBonds();
}
// Show fee
@ -492,14 +492,14 @@ public class DaoFacade implements DaoSetupService {
// Use case: Bonding
///////////////////////////////////////////////////////////////////////////////////////////
public void publishLockupTx(Coin lockupAmount, int lockTime, LockupType lockupType, byte[] hash,
public void publishLockupTx(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash,
Consumer<String> resultHandler, ExceptionHandler exceptionHandler) {
lockupService.publishLockupTx(lockupAmount, lockTime, lockupType, hash, resultHandler, exceptionHandler);
lockupTxService.publishLockupTx(lockupAmount, lockTime, lockupReason, hash, resultHandler, exceptionHandler);
}
public void publishUnlockTx(String lockupTxId, Consumer<String> resultHandler,
ExceptionHandler exceptionHandler) {
unlockService.publishUnlockTx(lockupTxId, resultHandler, exceptionHandler);
unlockTxService.publishUnlockTx(lockupTxId, resultHandler, exceptionHandler);
}
public long getTotalLockupAmount() {
@ -519,19 +519,19 @@ public class DaoFacade implements DaoSetupService {
}
public List<BondedRole> getActiveBondedRoles() {
return bondedRolesService.getActiveBonds();
return bondedRolesRepository.getActiveBonds();
}
public List<Bond> getAllBonds() {
List<BondedReputation> bondedReputations = bondedReputationService.getAllBonds();
List<BondedRole> bondedRoles = bondedRolesService.getAllBonds();
List<BondedReputation> bondedReputations = bondedReputationRepository.getBonds();
List<BondedRole> bondedRoles = bondedRolesRepository.getBonds();
List<Bond> bonds = new ArrayList<>(bondedReputations);
bonds.addAll(bondedRoles);
return bonds;
}
public List<MyBondedReputation> getMyBondedReputations() {
return myBondedReputationService.getMyBondedReputations();
return myBondedReputationRepository.getMyBondedReputations();
}
@ -661,7 +661,7 @@ public class DaoFacade implements DaoSetupService {
}
public boolean isMyRole(Role role) {
return bondedRolesService.isMyRole(role);
return bondedRolesRepository.isMyRole(role);
}
public Optional<Bond> getBondByLockupTxId(String lockupTxId) {

View File

@ -26,12 +26,12 @@ import bisq.core.dao.governance.blindvote.MyBlindVoteListService;
import bisq.core.dao.governance.blindvote.network.RepublishGovernanceDataHandler;
import bisq.core.dao.governance.blindvote.storage.BlindVoteStorageService;
import bisq.core.dao.governance.blindvote.storage.BlindVoteStore;
import bisq.core.dao.governance.bond.lockup.LockupService;
import bisq.core.dao.governance.bond.reputation.BondedReputationService;
import bisq.core.dao.governance.bond.reputation.MyBondedReputationService;
import bisq.core.dao.governance.bond.lockup.LockupTxService;
import bisq.core.dao.governance.bond.reputation.BondedReputationRepository;
import bisq.core.dao.governance.bond.reputation.MyBondedReputationRepository;
import bisq.core.dao.governance.bond.reputation.MyReputationListService;
import bisq.core.dao.governance.bond.role.BondedRolesService;
import bisq.core.dao.governance.bond.unlock.UnlockService;
import bisq.core.dao.governance.bond.role.BondedRolesRepository;
import bisq.core.dao.governance.bond.unlock.UnlockTxService;
import bisq.core.dao.governance.myvote.MyVoteListService;
import bisq.core.dao.governance.period.CycleService;
import bisq.core.dao.governance.period.PeriodService;
@ -186,12 +186,12 @@ public class DaoModule extends AppModule {
bind(Integer.class).annotatedWith(Names.named(DaoOptionKeys.GENESIS_BLOCK_HEIGHT)).toInstance(genesisBlockHeight);
// Bonds
bind(LockupService.class).in(Singleton.class);
bind(UnlockService.class).in(Singleton.class);
bind(BondedRolesService.class).in(Singleton.class);
bind(BondedReputationService.class).in(Singleton.class);
bind(LockupTxService.class).in(Singleton.class);
bind(UnlockTxService.class).in(Singleton.class);
bind(BondedRolesRepository.class).in(Singleton.class);
bind(BondedReputationRepository.class).in(Singleton.class);
bind(MyReputationListService.class).in(Singleton.class);
bind(MyBondedReputationService.class).in(Singleton.class);
bind(MyBondedReputationRepository.class).in(Singleton.class);
// Asset
bind(AssetService.class).in(Singleton.class);

View File

@ -20,10 +20,10 @@ package bisq.core.dao;
import bisq.core.dao.governance.ballot.BallotListService;
import bisq.core.dao.governance.blindvote.BlindVoteListService;
import bisq.core.dao.governance.blindvote.MyBlindVoteListService;
import bisq.core.dao.governance.bond.reputation.BondedReputationService;
import bisq.core.dao.governance.bond.reputation.MyBondedReputationService;
import bisq.core.dao.governance.bond.reputation.BondedReputationRepository;
import bisq.core.dao.governance.bond.reputation.MyBondedReputationRepository;
import bisq.core.dao.governance.bond.reputation.MyReputationListService;
import bisq.core.dao.governance.bond.role.BondedRolesService;
import bisq.core.dao.governance.bond.role.BondedRolesRepository;
import bisq.core.dao.governance.period.CycleService;
import bisq.core.dao.governance.proposal.ProposalService;
import bisq.core.dao.governance.voteresult.MissingDataRequestService;
@ -60,10 +60,10 @@ public class DaoSetup {
VoteRevealService voteRevealService,
VoteResultService voteResultService,
MissingDataRequestService missingDataRequestService,
BondedReputationService bondedReputationService,
BondedRolesService bondedRolesService,
BondedReputationRepository bondedReputationRepository,
BondedRolesRepository bondedRolesRepository,
MyReputationListService myReputationListService,
MyBondedReputationService myBondedReputationService,
MyBondedReputationRepository myBondedReputationRepository,
DaoFacade daoFacade,
ExportJsonFilesService exportJsonFilesService) {
@ -79,10 +79,10 @@ public class DaoSetup {
daoSetupServices.add(voteRevealService);
daoSetupServices.add(voteResultService);
daoSetupServices.add(missingDataRequestService);
daoSetupServices.add(bondedReputationService);
daoSetupServices.add(bondedRolesService);
daoSetupServices.add(bondedReputationRepository);
daoSetupServices.add(bondedRolesRepository);
daoSetupServices.add(myReputationListService);
daoSetupServices.add(myBondedReputationService);
daoSetupServices.add(myBondedReputationRepository);
daoSetupServices.add(daoFacade);
daoSetupServices.add(exportJsonFilesService);
daoSetupServices.add(bsqNodeProvider.getBsqNode());

View File

@ -17,7 +17,7 @@
package bisq.core.dao.governance.bond;
import bisq.core.locale.Res;
import java.util.Objects;
import lombok.Getter;
import lombok.Setter;
@ -25,7 +25,7 @@ import lombok.Setter;
import javax.annotation.Nullable;
/**
* Base class for BondedRole and BondedAsset. Holds the bond state of the bonded asset.
* Base class for BondedRole and BondedReputation. Holds the state of the bonded asset.
*/
@Getter
public abstract class Bond<T extends BondedAsset> {
@ -48,8 +48,7 @@ public abstract class Bond<T extends BondedAsset> {
@Setter
private int lockTime;
public Bond(T bondedAsset) {
protected Bond(T bondedAsset) {
this.bondedAsset = bondedAsset;
}
@ -58,8 +57,26 @@ public abstract class Bond<T extends BondedAsset> {
bondState != BondState.UNLOCKED;
}
public String getDisplayString() {
return Res.get("dao.bonding.info", lockupTxId, getBondedAsset().getDisplayString());
// Enums must not be used directly for hashCode or equals as it delivers the Object.hashCode (internal address)!
// The equals and hashCode methods cannot be overwritten in Enums.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Bond)) return false;
Bond<?> bond = (Bond<?>) o;
return amount == bond.amount &&
lockupDate == bond.lockupDate &&
unlockDate == bond.unlockDate &&
lockTime == bond.lockTime &&
Objects.equals(bondedAsset, bond.bondedAsset) &&
Objects.equals(lockupTxId, bond.lockupTxId) &&
Objects.equals(unlockTxId, bond.unlockTxId) &&
bondState.name().equals(bond.bondState.name());
}
@Override
public int hashCode() {
return Objects.hash(bondedAsset, lockupTxId, unlockTxId, bondState.name(), amount, lockupDate, unlockDate, lockTime);
}
@Override
@ -72,6 +89,7 @@ public abstract class Bond<T extends BondedAsset> {
",\n amount=" + amount +
",\n lockupDate=" + lockupDate +
",\n unlockDate=" + unlockDate +
",\n lockTime=" + lockTime +
"\n}";
}
}

View File

@ -17,7 +17,7 @@
package bisq.core.dao.governance.bond;
import bisq.core.dao.governance.bond.lockup.LockupType;
import bisq.core.dao.governance.bond.lockup.LockupReason;
import bisq.core.dao.state.model.blockchain.OpReturnType;
import bisq.common.app.Version;
@ -44,13 +44,13 @@ public class BondConsensus {
@Getter
private static int maxLockTime = 65535;
public static byte[] getLockupOpReturnData(int lockTime, LockupType type, byte[] hash) throws IOException {
public static byte[] getLockupOpReturnData(int lockTime, LockupReason type, byte[] hash) throws IOException {
// PushData of <= 4 bytes is converted to int when returned from bitcoind and not handled the way we
// require by btcd-cli4j, avoid opReturns with 4 bytes or less
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
outputStream.write(OpReturnType.LOCKUP.getType());
outputStream.write(Version.LOCKUP);
outputStream.write(type.getType());
outputStream.write(type.getId());
byte[] bytes = Utilities.integerToByteArray(lockTime, 2);
outputStream.write(bytes[0]);
outputStream.write(bytes[1]);
@ -72,19 +72,19 @@ public class BondConsensus {
return Utilities.byteArrayToInteger(Arrays.copyOfRange(opReturnData, 3, 5));
}
public static byte[] getHashFromOpReturnData(byte[] opReturnData) {
return Arrays.copyOfRange(opReturnData, 5, 25);
}
public static boolean isLockTimeInValidRange(int lockTime) {
return lockTime >= BondConsensus.getMinLockTime() && lockTime <= BondConsensus.getMaxLockTime();
}
public static Optional<LockupType> getLockupType(byte[] opReturnData) {
return LockupType.getLockupType(opReturnData[2]);
public static Optional<LockupReason> getLockupReason(byte[] opReturnData) {
return LockupReason.getLockupReason(opReturnData[2]);
}
public static boolean isLockTimeOver(long unlockBlockHeight, long currentBlockHeight) {
return currentBlockHeight >= unlockBlockHeight;
}
public static byte[] getHashFromOpReturnData(byte[] opReturnData) {
return Arrays.copyOfRange(opReturnData, 5, 25);
}
}

View File

@ -40,95 +40,21 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Manages bonds.
* Collect bonds and bond asset data from other sources and provides access to the collection.
*/
@Slf4j
public abstract class BondService<T extends Bond, R extends BondedAsset> implements DaoStateListener, DaoSetupService {
protected final DaoStateService daoStateService;
protected final BsqWalletService bsqWalletService;
// This map is just for convenience. The data which are used to fill the map are stored in the DaoState (role, txs).
protected final Map<String, T> bondByUidMap = new HashMap<>();
public abstract class BondRepository<T extends Bond, R extends BondedAsset> implements DaoStateListener, DaoSetupService {
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
// Static
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public BondService(DaoStateService daoStateService, BsqWalletService bsqWalletService) {
this.daoStateService = daoStateService;
this.bsqWalletService = bsqWalletService;
daoStateService.addBsqStateListener(this);
}
///////////////////////////////////////////////////////////////////////////////////////////
// DaoSetupService
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void addListeners() {
}
@Override
public void start() {
updateMap();
}
///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onNewBlockHeight(int blockHeight) {
}
@Override
public void onParseTxsComplete(Block block) {
// TODO optimize to not re-write the whole map at each block
updateMap();
}
protected void updateMap() {
getBondedAssetStream().forEach(bondedAsset -> {
String uid = bondedAsset.getUid();
bondByUidMap.putIfAbsent(uid, createBond(bondedAsset));
T bond = bondByUidMap.get(uid);
daoStateService.getLockupTxOutputs().forEach(lockupTxOutput -> {
updateBond(bond, bondedAsset, lockupTxOutput);
});
});
updateBondStateFromUnconfirmedLockupTxs();
updateBondStateFromUnconfirmedUnlockTxs();
}
public void updateBond(T bond, R bondedAsset, TxOutput lockupTxOutput) {
// Lets see if we have a lock up tx.
String lockupTxId = lockupTxOutput.getTxId();
daoStateService.getTx(lockupTxId).ifPresent(lockupTx -> {
byte[] opReturnData = lockupTx.getLastTxOutput().getOpReturnData();
// We used the hash of th bonded bondedAsset object as our hash in OpReturn of the lock up tx to have a
// unique binding of the tx to the data object.
byte[] hash = BondConsensus.getHashFromOpReturnData(opReturnData);
Optional<R> candidate = findBondedAssetByHash(hash);
if (candidate.isPresent() && bondedAsset.equals(candidate.get()))
applyBondState(daoStateService, bond, lockupTx, lockupTxOutput);
});
}
public static void applyBondState(DaoStateService daoStateService, Bond bond, Tx lockupTx, TxOutput lockupTxOutput) {
if (bond.getBondState() != BondState.LOCKUP_TX_PENDING || bond.getBondState() != BondState.UNLOCK_TX_PENDING)
bond.setBondState(BondState.LOCKUP_TX_CONFIRMED);
@ -166,89 +92,6 @@ public abstract class BondService<T extends Bond, R extends BondedAsset> impleme
}
}
protected abstract T createBond(R bondedAsset);
@Override
public void onParseBlockChainComplete() {
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public List<T> getBonds() {
return new ArrayList<>(bondByUidMap.values());
}
public List<T> getActiveBonds() {
return bondByUidMap.values().stream()
.filter(T::isActive)
.collect(Collectors.toList());
}
public List<T> getAllBonds() {
return new ArrayList<>(bondByUidMap.values());
}
public Optional<T> findBondByLockupTxId(String lockupTxId) {
return bondByUidMap.values().stream()
.filter(bond -> lockupTxId.equals(bond.getLockupTxId()))
.findAny();
}
public boolean wasBondedAssetAlreadyBonded(R bondedAsset) {
T bond = bondByUidMap.get(bondedAsset.getUid());
checkArgument(bond != null, "bond must not be null");
return bond.getLockupTxId() != null;
}
public Optional<R> findBondedAssetByHash(byte[] hash) {
return getBondedAssetStream()
.filter(bondedAsset -> Arrays.equals(bondedAsset.getHash(), hash))
.findAny();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Protected
///////////////////////////////////////////////////////////////////////////////////////////
abstract protected Stream<R> getBondedAssetStream();
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void updateBondStateFromUnconfirmedLockupTxs() {
getBondedAssetStream().filter(bondedAsset -> isLockupTxUnconfirmed(bsqWalletService, bondedAsset))
.map(bondedAsset -> bondByUidMap.get(bondedAsset.getUid()))
.filter(bond -> bond.getBondState() == BondState.READY_FOR_LOCKUP)
.forEach(bond -> {
if ((bond.getLockupTxId() != null && daoStateService.isConfiscatedLockupTxOutput(bond.getLockupTxId())) ||
(bond.getUnlockTxId() != null && daoStateService.isConfiscatedUnlockTxOutput(bond.getUnlockTxId()))) {
bond.setBondState(BondState.CONFISCATED);
} else {
bond.setBondState(BondState.LOCKUP_TX_PENDING);
}
});
}
private void updateBondStateFromUnconfirmedUnlockTxs() {
getBondedAssetStream().filter(bondedAsset -> isUnlockTxUnconfirmed(bsqWalletService, daoStateService, bondedAsset))
.map(bondedAsset -> bondByUidMap.get(bondedAsset.getUid()))
.filter(bond -> bond.getBondState() == BondState.LOCKUP_TX_CONFIRMED)
.forEach(bond -> {
if ((bond.getLockupTxId() != null && daoStateService.isConfiscatedLockupTxOutput(bond.getLockupTxId())) ||
(bond.getUnlockTxId() != null && daoStateService.isConfiscatedUnlockTxOutput(bond.getUnlockTxId()))) {
bond.setBondState(BondState.CONFISCATED);
} else {
bond.setBondState(BondState.UNLOCK_TX_PENDING);
}
});
}
public static boolean isLockupTxUnconfirmed(BsqWalletService bsqWalletService, BondedAsset bondedAsset) {
return bsqWalletService.getPendingWalletTransactionsStream()
.map(transaction -> transaction.getOutputs().get(transaction.getOutputs().size() - 1))
@ -274,4 +117,127 @@ public abstract class BondService<T extends Bond, R extends BondedAsset> impleme
.map(BaseTxOutput::getOpReturnData)
.anyMatch(data -> Arrays.equals(BondConsensus.getHashFromOpReturnData(data), bondedAsset.getHash()));
}
public static boolean isConfiscated(Bond bond, DaoStateService daoStateService) {
return (bond.getLockupTxId() != null && daoStateService.isConfiscatedLockupTxOutput(bond.getLockupTxId())) ||
(bond.getUnlockTxId() != null && daoStateService.isConfiscatedUnlockTxOutput(bond.getUnlockTxId()));
}
protected final DaoStateService daoStateService;
protected final BsqWalletService bsqWalletService;
// This map is just for convenience. The data which are used to fill the map are stored in the DaoState (role, txs).
protected final Map<String, T> bondByUidMap = new HashMap<>();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public BondRepository(DaoStateService daoStateService, BsqWalletService bsqWalletService) {
this.daoStateService = daoStateService;
this.bsqWalletService = bsqWalletService;
daoStateService.addBsqStateListener(this);
}
///////////////////////////////////////////////////////////////////////////////////////////
// DaoSetupService
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void addListeners() {
}
@Override
public void start() {
updateMap();
}
///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onNewBlockHeight(int blockHeight) {
}
@Override
public void onParseTxsComplete(Block block) {
// TODO optimize to not re-write the whole map at each block
updateMap();
}
@Override
public void onParseBlockChainComplete() {
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public List<T> getBonds() {
return new ArrayList<>(bondByUidMap.values());
}
public List<T> getActiveBonds() {
return bondByUidMap.values().stream()
.filter(T::isActive)
.collect(Collectors.toList());
}
public boolean isBondedAssetAlreadyInBond(R bondedAsset) {
boolean contains = bondByUidMap.containsKey(bondedAsset.getUid());
return contains && bondByUidMap.get(bondedAsset.getUid()).getLockupTxId() != null;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Protected
///////////////////////////////////////////////////////////////////////////////////////////
abstract protected T createBond(R bondedAsset);
abstract protected void updateBond(T bond, R bondedAsset, TxOutput lockupTxOutput);
abstract protected Stream<R> getBondedAssetStream();
protected void updateMap() {
getBondedAssetStream().forEach(bondedAsset -> {
String uid = bondedAsset.getUid();
bondByUidMap.putIfAbsent(uid, createBond(bondedAsset));
T bond = bondByUidMap.get(uid);
daoStateService.getLockupTxOutputs().forEach(lockupTxOutput -> {
updateBond(bond, bondedAsset, lockupTxOutput);
});
});
updateBondStateFromUnconfirmedLockupTxs();
updateBondStateFromUnconfirmedUnlockTxs();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void updateBondStateFromUnconfirmedLockupTxs() {
getBondedAssetStream().filter(bondedAsset -> isLockupTxUnconfirmed(bsqWalletService, bondedAsset))
.map(bondedAsset -> bondByUidMap.get(bondedAsset.getUid()))
.filter(bond -> bond.getBondState() == BondState.READY_FOR_LOCKUP)
.forEach(bond -> bond.setBondState(isConfiscated(bond, daoStateService) ? BondState.CONFISCATED : BondState.LOCKUP_TX_PENDING));
}
private void updateBondStateFromUnconfirmedUnlockTxs() {
getBondedAssetStream().filter(bondedAsset -> isUnlockTxUnconfirmed(bsqWalletService, daoStateService, bondedAsset))
.map(bondedAsset -> bondByUidMap.get(bondedAsset.getUid()))
.filter(bond -> bond.getBondState() == BondState.LOCKUP_TX_CONFIRMED)
.forEach(bond -> bond.setBondState(isConfiscated(bond, daoStateService) ? BondState.CONFISCATED : BondState.UNLOCK_TX_PENDING));
}
}

View File

@ -17,14 +17,17 @@
package bisq.core.dao.governance.bond;
// Used in string properties ("dao.bond.bondState.*")
/**
* Holds the different states of a bond.
* Used also in string properties ("dao.bond.bondState.*")
*/
public enum BondState {
READY_FOR_LOCKUP, // Accepted by voting but no lockup tx made yet.
READY_FOR_LOCKUP, // Accepted by voting (if role) but no lockup tx made yet.
LOCKUP_TX_PENDING, // Tx broadcasted but not confirmed. Used only by tx publisher.
LOCKUP_TX_CONFIRMED,
UNLOCK_TX_PENDING, // Tx broadcasted but not confirmed. Used only by tx publisher.
UNLOCK_TX_CONFIRMED,
UNLOCKING, // lock time not expired
UNLOCKED,
CONFISCATED
UNLOCKING, // Lock time still not expired
UNLOCKED, // Fully unlocked
CONFISCATED // Bond got confiscated by DAO voting
}

View File

@ -18,7 +18,7 @@
package bisq.core.dao.governance.bond;
/**
* Interface of the bonded asset like the Role or Reputation.
* Represents the bonded asset (e.g. Role or Reputation).
*/
public interface BondedAsset {
byte[] getHash();

View File

@ -1,63 +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 bisq.core.dao.governance.bond;
import bisq.common.proto.persistable.PersistablePayload;
import io.bisq.generated.protobuffer.PB;
import lombok.Value;
import javax.annotation.concurrent.Immutable;
/**
* Holds the hash of a confiscated bond.
*/
@Immutable
@Value
public class ConfiscateBond implements PersistablePayload {
private final String lockupTxId;
public ConfiscateBond(String lockupTxId) {
this.lockupTxId = lockupTxId;
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public PB.ConfiscateBond toProtoMessage() {
return PB.ConfiscateBond.newBuilder()
.setLockupTxId(lockupTxId)
.build();
}
public static ConfiscateBond fromProto(PB.ConfiscateBond proto) {
return new ConfiscateBond(proto.getLockupTxId());
}
@Override
public String toString() {
return "ConfiscateBond{" +
"\n lockupTxId='" + lockupTxId + '\'' +
"\n}";
}
}

View File

@ -17,33 +17,32 @@
package bisq.core.dao.governance.bond.lockup;
import bisq.core.locale.Res;
import java.util.Arrays;
import java.util.Optional;
import lombok.Getter;
public enum LockupType {
/**
* Reason for locking up a bond.
*/
public enum LockupReason {
BONDED_ROLE((byte) 0x01),
REPUTATION((byte) 0x02);
@Getter
private byte type;
private byte id;
LockupType(byte type) {
this.type = type;
LockupReason(byte id) {
this.id = id;
}
public String getDisplayString() {
/* public String getDisplayString() {
return Res.get("dao.bond.lockupType." + name());
}
}*/
public static Optional<LockupType> getLockupType(byte type) {
return Arrays.stream(LockupType.values())
.filter(lockupType -> lockupType.type == type)
.map(Optional::of)
.findAny()
.orElse(Optional.empty());
public static Optional<LockupReason> getLockupReason(byte id) {
return Arrays.stream(LockupReason.values())
.filter(lockupType -> lockupType.id == id)
.findAny();
}
}

View File

@ -43,8 +43,11 @@ import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Service for publishing the lockup transaction.
*/
@Slf4j
public class LockupService {
public class LockupTxService {
private final WalletsManager walletsManager;
private final BsqWalletService bsqWalletService;
private final BtcWalletService btcWalletService;
@ -55,23 +58,22 @@ public class LockupService {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public LockupService(WalletsManager walletsManager,
BsqWalletService bsqWalletService,
BtcWalletService btcWalletService) {
public LockupTxService(WalletsManager walletsManager,
BsqWalletService bsqWalletService,
BtcWalletService btcWalletService) {
this.walletsManager = walletsManager;
this.bsqWalletService = bsqWalletService;
this.btcWalletService = btcWalletService;
}
public void publishLockupTx(Coin lockupAmount, int lockTime, LockupType lockupType, byte[] hash,
public void publishLockupTx(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash,
Consumer<String> resultHandler, ExceptionHandler exceptionHandler) {
checkArgument(lockTime <= BondConsensus.getMaxLockTime() &&
lockTime >= BondConsensus.getMinLockTime(), "lockTime not in rage");
lockTime >= BondConsensus.getMinLockTime(), "lockTime not in range");
try {
byte[] opReturnData = BondConsensus.getLockupOpReturnData(lockTime, lockupType, hash);
byte[] opReturnData = BondConsensus.getLockupOpReturnData(lockTime, lockupReason, hash);
Transaction lockupTx = createLockupTx(lockupAmount, opReturnData);
//noinspection Duplicates
walletsManager.publishAndCommitBsqTx(lockupTx, new TxBroadcaster.Callback() {
@Override
public void onSuccess(Transaction transaction) {
@ -99,6 +101,8 @@ public class LockupService {
throws InsufficientMoneyException, WalletException, TransactionVerificationException {
Transaction preparedTx = bsqWalletService.getPreparedLockupTx(lockupAmount);
Transaction txWithBtcFee = btcWalletService.completePreparedBsqTx(preparedTx, true, opReturnData);
return bsqWalletService.signTx(txWithBtcFee);
Transaction transaction = bsqWalletService.signTx(txWithBtcFee);
log.info("Lockup tx: " + transaction);
return transaction;
}
}

View File

@ -29,11 +29,10 @@ import lombok.Getter;
@EqualsAndHashCode(callSuper = true)
public final class BondedReputation extends Bond<Reputation> {
public BondedReputation(Reputation reputation) {
BondedReputation(Reputation reputation) {
super(reputation);
}
@Override
public String toString() {
return "BondedReputation{" +

View File

@ -20,8 +20,8 @@ package bisq.core.dao.governance.bond.reputation;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.governance.bond.Bond;
import bisq.core.dao.governance.bond.BondConsensus;
import bisq.core.dao.governance.bond.BondService;
import bisq.core.dao.governance.bond.role.BondedRolesService;
import bisq.core.dao.governance.bond.BondRepository;
import bisq.core.dao.governance.bond.role.BondedRolesRepository;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.TxOutput;
@ -35,20 +35,24 @@ import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
/**
* Collect bonded reputations from the daoState blockchain data excluding bonded roles
* and provides access to the collection.
*/
@Slf4j
public class BondedReputationService extends BondService<BondedReputation, Reputation> {
private final BondedRolesService bondedRolesService;
public class BondedReputationRepository extends BondRepository<BondedReputation, Reputation> {
private final BondedRolesRepository bondedRolesRepository;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public BondedReputationService(DaoStateService daoStateService, BsqWalletService bsqWalletService,
BondedRolesService bondedRolesService) {
public BondedReputationRepository(DaoStateService daoStateService, BsqWalletService bsqWalletService,
BondedRolesRepository bondedRolesRepository) {
super(daoStateService, bsqWalletService);
this.bondedRolesService = bondedRolesService;
this.bondedRolesRepository = bondedRolesRepository;
}
@ -69,18 +73,13 @@ public class BondedReputationService extends BondService<BondedReputation, Reput
@Override
protected void updateMap() {
bondByUidMap.clear();
getBondedReputationStream().forEach(bondedReputation -> {
bondByUidMap.put(bondedReputation.getBondedAsset().getUid(), bondedReputation);
});
getBondedReputationStream().forEach(bondedReputation -> bondByUidMap.put(bondedReputation.getBondedAsset().getUid(), bondedReputation));
}
private Stream<BondedReputation> getBondedReputationStream() {
Set<String> bondedRolesLockupTxIdSet = bondedRolesService.getAllBonds().stream().map(e -> e.getLockupTxId()).collect(Collectors.toSet());
return daoStateService.getLockupTxOutputs().stream()
return getLockupTxOutputsForBondedReputation()
.map(lockupTxOutput -> {
String lockupTxId = lockupTxOutput.getTxId();
// long time = daoStateService.getTx(lockupTxId).map(BaseTx::getTime).orElse(0L);
// lockupTxOutput is first output, but we need the data from the opReturn
Optional<TxOutput> optionalOpReturnTxOutput = daoStateService.getLockupOpReturnTxOutput(lockupTxId);
if (optionalOpReturnTxOutput.isPresent()) {
TxOutput opReturnTxOutput = optionalOpReturnTxOutput.get();
@ -94,16 +93,22 @@ public class BondedReputationService extends BondService<BondedReputation, Reput
}
})
.filter(Objects::nonNull)
.filter(e -> !bondedRolesLockupTxIdSet.contains(e.getLockupTxId()));
.filter(Objects::nonNull);
}
private Stream<TxOutput> getLockupTxOutputsForBondedReputation() {
// We exclude bonded roles, so we store those ina lookup set.
Set<String> bondedRolesLockupTxIdSet = bondedRolesRepository.getBonds().stream().map(Bond::getLockupTxId).collect(Collectors.toSet());
return daoStateService.getLockupTxOutputs().stream()
.filter(e -> !bondedRolesLockupTxIdSet.contains(e.getTxId()));
}
@Override
public void updateBond(BondedReputation bond, Reputation bondedAsset, TxOutput lockupTxOutput) {
protected void updateBond(BondedReputation bond, Reputation bondedAsset, TxOutput lockupTxOutput) {
// Lets see if we have a lock up tx.
String lockupTxId = lockupTxOutput.getTxId();
daoStateService.getTx(lockupTxId).ifPresent(lockupTx -> {
applyBondState(daoStateService, bond, lockupTx, lockupTxOutput);
BondRepository.applyBondState(daoStateService, bond, lockupTx, lockupTxOutput);
});
}
}

View File

@ -23,7 +23,8 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
/**
* Wrapper for reputation which contains the mutable state of a bonded reputation. Only kept in memory.
* Wrapper for reputation which contains the mutable state of my bonded reputation. Only kept in memory.
* As it carries MyReputation it has access to the private salt data.
*/
@Getter
@EqualsAndHashCode(callSuper = true)

View File

@ -20,7 +20,7 @@ package bisq.core.dao.governance.bond.reputation;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.DaoSetupService;
import bisq.core.dao.governance.bond.BondConsensus;
import bisq.core.dao.governance.bond.BondService;
import bisq.core.dao.governance.bond.BondRepository;
import bisq.core.dao.governance.bond.BondState;
import bisq.core.dao.state.DaoStateService;
@ -36,8 +36,11 @@ import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
/**
* Collect MyBondedReputations from the myReputationListService and provides access to the collection.
*/
@Slf4j
public class MyBondedReputationService implements DaoSetupService {
public class MyBondedReputationRepository implements DaoSetupService {
private final DaoStateService daoStateService;
private final BsqWalletService bsqWalletService;
private final MyReputationListService myReputationListService;
@ -48,9 +51,9 @@ public class MyBondedReputationService implements DaoSetupService {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public MyBondedReputationService(DaoStateService daoStateService,
BsqWalletService bsqWalletService,
MyReputationListService myReputationListService) {
public MyBondedReputationRepository(DaoStateService daoStateService,
BsqWalletService bsqWalletService,
MyReputationListService myReputationListService) {
this.daoStateService = daoStateService;
this.bsqWalletService = bsqWalletService;
this.myReputationListService = myReputationListService;
@ -75,25 +78,24 @@ public class MyBondedReputationService implements DaoSetupService {
///////////////////////////////////////////////////////////////////////////////////////////
public List<MyBondedReputation> getMyBondedReputations() {
// It can be that the same salt/hash is in several lockupTxs, so we use a map to eliminate duplicates by the
// collection algorithm.
Map<String, MyBondedReputation> map = new HashMap<>();
// It can be that the same salt/hash is in several lockupTxs, so we use the bondByLockupTxIdMap to eliminate
// duplicates by the collection algorithm.
Map<String, MyBondedReputation> bondByLockupTxIdMap = new HashMap<>();
myReputationListService.getMyReputationList().stream()
.flatMap(this::getMyBondedReputation)
.forEach(e -> map.putIfAbsent(e.getLockupTxId(), e));
.forEach(e -> bondByLockupTxIdMap.putIfAbsent(e.getLockupTxId(), e));
return map.values().stream()
return bondByLockupTxIdMap.values().stream()
.peek(myBondedReputation -> {
if ((myBondedReputation.getLockupTxId() != null && daoStateService.isConfiscatedLockupTxOutput(myBondedReputation.getLockupTxId())) ||
(myBondedReputation.getUnlockTxId() != null && daoStateService.isConfiscatedUnlockTxOutput(myBondedReputation.getUnlockTxId()))) {
if (BondRepository.isConfiscated(myBondedReputation, daoStateService)) {
myBondedReputation.setBondState(BondState.CONFISCATED);
} else {
// We don't have a UI use case for showing LOCKUP_TX_PENDING yet, but lets keep the code so if needed
// its there.
if (BondService.isLockupTxUnconfirmed(bsqWalletService, myBondedReputation.getBondedAsset()) &&
if (BondRepository.isLockupTxUnconfirmed(bsqWalletService, myBondedReputation.getBondedAsset()) &&
myBondedReputation.getBondState() == BondState.READY_FOR_LOCKUP) {
myBondedReputation.setBondState(BondState.LOCKUP_TX_PENDING);
} else if (BondService.isUnlockTxUnconfirmed(bsqWalletService, daoStateService, myBondedReputation.getBondedAsset()) &&
} else if (BondRepository.isUnlockTxUnconfirmed(bsqWalletService, daoStateService, myBondedReputation.getBondedAsset()) &&
myBondedReputation.getBondState() == BondState.LOCKUP_TX_CONFIRMED) {
myBondedReputation.setBondState(BondState.UNLOCK_TX_PENDING);
}
@ -110,9 +112,10 @@ public class MyBondedReputationService implements DaoSetupService {
.map(lockupTx -> {
byte[] opReturnData = lockupTx.getLastTxOutput().getOpReturnData();
byte[] hash = BondConsensus.getHashFromOpReturnData(opReturnData);
// There could be multiple txs with the same hash, so we collect a stream and not use an optional.
if (Arrays.equals(hash, myReputation.getHash())) {
MyBondedReputation myBondedReputation = new MyBondedReputation(myReputation);
BondService.applyBondState(daoStateService, myBondedReputation, lockupTx, lockupTxOutput);
BondRepository.applyBondState(daoStateService, myBondedReputation, lockupTx, lockupTxOutput);
return myBondedReputation;
} else {
return null;

View File

@ -37,13 +37,18 @@ import lombok.extern.slf4j.Slf4j;
import javax.annotation.concurrent.Immutable;
/**
* MyReputation is persisted locally and carries the private salt data. In contrast to Reputation which is the public
* data everyone can derive from the blockchain data (hash in opReturn).
*/
@Immutable
@Value
@Slf4j
public final class MyReputation implements PersistablePayload, NetworkPayload, BondedAsset {
// Uid is needed to be sure that 2 objects with the same salt are kept separate.
private final String uid;
private final byte[] salt;
private final transient byte[] hash; // not persisted as it is derived from salt. Stored for caching purpose only.
private final transient byte[] hash; // Not persisted as it is derived from salt. Stored for caching purpose only.
public MyReputation(byte[] salt) {
this(UUID.randomUUID().toString(), salt);

View File

@ -33,11 +33,11 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
public class MyReputationList extends PersistableList<MyReputation> {
public MyReputationList(List<MyReputation> list) {
private MyReputationList(List<MyReputation> list) {
super(list);
}
public MyReputationList() {
MyReputationList() {
super();
}
@ -51,7 +51,7 @@ public class MyReputationList extends PersistableList<MyReputation> {
return PB.PersistableEnvelope.newBuilder().setMyReputationList(getBuilder()).build();
}
public PB.MyReputationList.Builder getBuilder() {
private PB.MyReputationList.Builder getBuilder() {
return PB.MyReputationList.newBuilder()
.addAllMyReputation(getList().stream()
.map(MyReputation::toProtoMessage)

View File

@ -31,10 +31,14 @@ import java.util.concurrent.CopyOnWriteArrayList;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
/**
* Manages the persistence of myReputation objects.
*/
@Slf4j
public class MyReputationListService implements PersistedDataHost, DaoSetupService {
public interface MyReputationListChangeListener {
@SuppressWarnings("unused")
interface MyReputationListChangeListener {
void onListChanged(List<MyReputation> list);
}
@ -88,10 +92,6 @@ public class MyReputationListService implements PersistedDataHost, DaoSetupServi
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void addListener(MyReputationListChangeListener listener) {
listeners.add(listener);
}
public void addReputation(MyReputation reputation) {
if (!myReputationList.contains(reputation)) {
myReputationList.add(reputation);
@ -103,6 +103,11 @@ public class MyReputationListService implements PersistedDataHost, DaoSetupServi
return myReputationList.getList();
}
@SuppressWarnings("unused")
public void addListener(MyReputationListChangeListener listener) {
listeners.add(listener);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private

View File

@ -30,6 +30,8 @@ import javax.annotation.concurrent.Immutable;
/**
* Reputation objects we found on the blockchain. We only know the hash of it.
* In contrast to MyReputation which represents the object we created and contains the
* private salt data.
*/
@Immutable
@Value

View File

@ -18,10 +18,10 @@
package bisq.core.dao.governance.bond.role;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.governance.bond.Bond;
import bisq.core.dao.governance.bond.BondService;
import bisq.core.dao.governance.bond.BondConsensus;
import bisq.core.dao.governance.bond.BondRepository;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.governance.BondedRoleType;
import bisq.core.dao.state.model.blockchain.TxOutput;
import bisq.core.dao.state.model.governance.Proposal;
import bisq.core.dao.state.model.governance.Role;
import bisq.core.dao.state.model.governance.RoleProposal;
@ -30,6 +30,7 @@ import org.bitcoinj.core.Transaction;
import javax.inject.Inject;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@ -38,17 +39,17 @@ import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
/**
* Manages bonded roles if they got accepted by voting.
* Collect bonded roles from the evaluatedProposals from the daoState and provides access to the collection.
*/
@Slf4j
public class BondedRolesService extends BondService<BondedRole, Role> {
public class BondedRolesRepository extends BondRepository<BondedRole, Role> {
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public BondedRolesService(DaoStateService daoStateService, BsqWalletService bsqWalletService) {
public BondedRolesRepository(DaoStateService daoStateService, BsqWalletService bsqWalletService) {
super(daoStateService, bsqWalletService);
}
@ -67,12 +68,6 @@ public class BondedRolesService extends BondService<BondedRole, Role> {
.anyMatch(myWalletTransactionIds::contains);
}
public Optional<BondedRoleType> getBondedRoleType(String lockUpTxId) {
return findBondByLockupTxId(lockUpTxId)
.map(Bond::getBondedAsset)
.map(Role::getBondedRoleType);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Protected
@ -88,6 +83,27 @@ public class BondedRolesService extends BondService<BondedRole, Role> {
return getBondedRoleProposalStream().map(RoleProposal::getRole);
}
@Override
protected void updateBond(BondedRole bond, Role bondedAsset, TxOutput lockupTxOutput) {
// Lets see if we have a lock up tx.
String lockupTxId = lockupTxOutput.getTxId();
daoStateService.getTx(lockupTxId).ifPresent(lockupTx -> {
byte[] opReturnData = lockupTx.getLastTxOutput().getOpReturnData();
// We used the hash of th bonded bondedAsset object as our hash in OpReturn of the lock up tx to have a
// unique binding of the tx to the data object.
byte[] hash = BondConsensus.getHashFromOpReturnData(opReturnData);
Optional<Role> candidate = findBondedAssetByHash(hash);
if (candidate.isPresent() && bondedAsset.equals(candidate.get()))
applyBondState(daoStateService, bond, lockupTx, lockupTxOutput);
});
}
private Optional<Role> findBondedAssetByHash(byte[] hash) {
return getBondedAssetStream()
.filter(bondedAsset -> Arrays.equals(bondedAsset.getHash(), hash))
.findAny();
}
private Stream<RoleProposal> getBondedRoleProposalStream() {
return daoStateService.getEvaluatedProposalList().stream()
.filter(evaluatedProposal -> evaluatedProposal.getProposal() instanceof RoleProposal)

View File

@ -35,12 +35,18 @@ import org.bitcoinj.core.Transaction;
import javax.inject.Inject;
import java.util.Optional;
import java.util.function.Consumer;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Service for publishing the unlock transaction.
*/
@Slf4j
public class UnlockService {
public class UnlockTxService {
private final WalletsManager walletsManager;
private final BsqWalletService bsqWalletService;
private final BtcWalletService btcWalletService;
@ -52,23 +58,22 @@ public class UnlockService {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public UnlockService(WalletsManager walletsManager,
BsqWalletService bsqWalletService,
BtcWalletService btcWalletService,
DaoStateService daoStateService) {
public UnlockTxService(WalletsManager walletsManager,
BsqWalletService bsqWalletService,
BtcWalletService btcWalletService,
DaoStateService daoStateService) {
this.walletsManager = walletsManager;
this.bsqWalletService = bsqWalletService;
this.btcWalletService = btcWalletService;
this.daoStateService = daoStateService;
}
public void publishUnlockTx(String lockupTxId, Consumer<String> resultHandler,
ExceptionHandler exceptionHandler) {
public void publishUnlockTx(String lockupTxId, Consumer<String> resultHandler, ExceptionHandler exceptionHandler) {
try {
TxOutput lockupTxOutput = daoStateService.getLockupTxOutput(lockupTxId).get();
final Transaction unlockTx = getUnlockTx(lockupTxOutput);
//noinspection Duplicates
Optional<TxOutput> optionalLockupTxOutput = daoStateService.getLockupTxOutput(lockupTxId);
checkArgument(optionalLockupTxOutput.isPresent(), "lockupTxOutput must be present");
TxOutput lockupTxOutput = optionalLockupTxOutput.get();
Transaction unlockTx = getUnlockTx(lockupTxOutput);
walletsManager.publishAndCommitBsqTx(unlockTx, new TxBroadcaster.Callback() {
@Override
public void onSuccess(Transaction transaction) {
@ -94,8 +99,7 @@ public class UnlockService {
throws InsufficientMoneyException, WalletException, TransactionVerificationException {
Transaction preparedTx = bsqWalletService.getPreparedUnlockTx(lockupTxOutput);
Transaction txWithBtcFee = btcWalletService.completePreparedBsqTx(preparedTx, true, null);
final Transaction transaction = bsqWalletService.signTx(txWithBtcFee);
Transaction transaction = bsqWalletService.signTx(txWithBtcFee);
log.info("Unlock tx: " + transaction);
return transaction;
}

View File

@ -25,8 +25,7 @@ import bisq.core.dao.governance.blindvote.BlindVoteConsensus;
import bisq.core.dao.governance.blindvote.BlindVoteListService;
import bisq.core.dao.governance.blindvote.VoteWithProposalTxId;
import bisq.core.dao.governance.blindvote.VoteWithProposalTxIdList;
import bisq.core.dao.governance.bond.ConfiscateBond;
import bisq.core.dao.governance.bond.role.BondedRolesService;
import bisq.core.dao.governance.bond.role.BondedRolesRepository;
import bisq.core.dao.governance.merit.MeritConsensus;
import bisq.core.dao.governance.period.PeriodService;
import bisq.core.dao.governance.proposal.IssuanceProposal;
@ -103,7 +102,7 @@ public class VoteResultService implements DaoStateListener, DaoSetupService {
private final PeriodService periodService;
private final BallotListService ballotListService;
private final BlindVoteListService blindVoteListService;
private final BondedRolesService bondedRolesService;
private final BondedRolesRepository bondedRolesRepository;
private final IssuanceService issuanceService;
private final AssetService assetService;
private final MissingDataRequestService missingDataRequestService;
@ -122,7 +121,7 @@ public class VoteResultService implements DaoStateListener, DaoSetupService {
PeriodService periodService,
BallotListService ballotListService,
BlindVoteListService blindVoteListService,
BondedRolesService bondedRolesService,
BondedRolesRepository bondedRolesRepository,
IssuanceService issuanceService,
AssetService assetService,
MissingDataRequestService missingDataRequestService) {
@ -132,7 +131,7 @@ public class VoteResultService implements DaoStateListener, DaoSetupService {
this.periodService = periodService;
this.ballotListService = ballotListService;
this.blindVoteListService = blindVoteListService;
this.bondedRolesService = bondedRolesService;
this.bondedRolesRepository = bondedRolesRepository;
this.issuanceService = issuanceService;
this.assetService = assetService;
this.missingDataRequestService = missingDataRequestService;
@ -661,7 +660,7 @@ public class VoteResultService implements DaoStateListener, DaoSetupService {
acceptedEvaluatedProposals.forEach(evaluatedProposal -> {
if (evaluatedProposal.getProposal() instanceof ConfiscateBondProposal) {
ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) evaluatedProposal.getProposal();
daoStateService.confiscateBond(new ConfiscateBond(confiscateBondProposal.getLockupTxId()));
daoStateService.confiscateBond(confiscateBondProposal.getLockupTxId());
StringBuilder sb = new StringBuilder();
sb.append("\n################################################################################\n");

View File

@ -19,7 +19,7 @@ package bisq.core.dao.node.parser;
import bisq.core.dao.governance.blindvote.BlindVoteConsensus;
import bisq.core.dao.governance.bond.BondConsensus;
import bisq.core.dao.governance.bond.lockup.LockupType;
import bisq.core.dao.governance.bond.lockup.LockupReason;
import bisq.core.dao.governance.proposal.ProposalConsensus;
import bisq.core.dao.governance.voteresult.VoteResultConsensus;
import bisq.core.dao.node.parser.exceptions.InvalidParsingConditionException;
@ -107,9 +107,9 @@ class OpReturnParser {
case LOCKUP:
if (!BondConsensus.hasOpReturnDataValidLength(opReturnData))
return TxOutputType.INVALID_OUTPUT;
Optional<LockupType> lockupType = BondConsensus.getLockupType(opReturnData);
if (!lockupType.isPresent()) {
log.warn("No lockupType found for lockup tx, opReturnData=" + Utilities.encodeToHex(opReturnData));
Optional<LockupReason> optionalLockupReason = BondConsensus.getLockupReason(opReturnData);
if (!optionalLockupReason.isPresent()) {
log.warn("No lockupReason found for lockup tx, opReturnData=" + Utilities.encodeToHex(opReturnData));
return TxOutputType.INVALID_OUTPUT;
}

View File

@ -19,7 +19,6 @@ package bisq.core.dao.state;
import bisq.core.dao.DaoSetupService;
import bisq.core.dao.governance.bond.BondConsensus;
import bisq.core.dao.governance.bond.ConfiscateBond;
import bisq.core.dao.governance.param.Param;
import bisq.core.dao.state.model.DaoState;
import bisq.core.dao.state.model.blockchain.Block;
@ -742,8 +741,7 @@ public class DaoStateService implements DaoSetupService {
}
// Confiscate bond
public void confiscateBond(ConfiscateBond confiscateBond) {
String lockupTxId = confiscateBond.getLockupTxId();
public void confiscateBond(String lockupTxId) {
Optional<TxOutput> optionalTxOutput = getLockupTxOutput(lockupTxId);
if (optionalTxOutput.isPresent()) {
TxOutput lockupTxOutput = optionalTxOutput.get();

View File

@ -21,10 +21,14 @@ import bisq.core.locale.Res;
import lombok.Getter;
// Data here must not be changed as it would break backward compatibility! In case we need to change we need to add a new
// entry and maintain the old one. Once all the role holders of an old deprecated role have revoked the role might get removed.
// Add entry to translation file "dao.bond.bondedRoleType...."
/**
* Data here must not be changed as it would break backward compatibility! In case we need to change we need to add a
* new entry and maintain the old one. Once all the role holders of an old deprecated role have revoked the
* role might get removed.
*
* Add entry to translation file "dao.bond.bondedRoleType...."
*/
public enum BondedRoleType {
// admins
GITHUB_ADMIN(50_000, 60, "https://github.com/bisq-network/roles/issues/16", true),

View File

@ -1339,9 +1339,9 @@ dao.bonding.info=Lockup Tx ID: {0} / {1}
dao.bonding.reputation.salt.info=Salt: {0}
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Bonded role
dao.bond.lockupReason.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Bonded reputation
dao.bond.lockupReason.REPUTATION=Bonded reputation
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.GITHUB_ADMIN=Github admin

View File

@ -1143,9 +1143,9 @@ dao.bonding.dashboard.lockupAmount=Gesperrte Gelder
dao.bonding.dashboard.unlockingAmount=Entsperre Gelder (Warten Sie bis die Sperrzeit vorbei ist):
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Gekoppelte Rolle
dao.bond.lockupReason.BONDED_ROLE=Gekoppelte Rolle
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Gekoppeltes Ansehen
dao.bond.lockupReason.REPUTATION=Gekoppeltes Ansehen
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.ARBITRATOR=Vermittler
# suppress inspection "UnusedProperty"

View File

@ -1143,9 +1143,9 @@ dao.bonding.dashboard.lockupAmount=Lockup funds:
dao.bonding.dashboard.unlockingAmount=Unlocking funds (wait until lock time is over):
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Bonded role
dao.bond.lockupReason.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Bonded reputation
dao.bond.lockupReason.REPUTATION=Bonded reputation
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.ARBITRATOR=Arbitrator
# suppress inspection "UnusedProperty"

View File

@ -1143,9 +1143,9 @@ dao.bonding.dashboard.lockupAmount=Lockup funds:
dao.bonding.dashboard.unlockingAmount=Unlocking funds (wait until lock time is over):
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Bonded role
dao.bond.lockupReason.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Bonded reputation
dao.bond.lockupReason.REPUTATION=Bonded reputation
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.ARBITRATOR=Arbitrator
# suppress inspection "UnusedProperty"

View File

@ -1143,9 +1143,9 @@ dao.bonding.dashboard.lockupAmount=Lockup funds:
dao.bonding.dashboard.unlockingAmount=Unlocking funds (wait until lock time is over):
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Bonded role
dao.bond.lockupReason.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Bonded reputation
dao.bond.lockupReason.REPUTATION=Bonded reputation
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.ARBITRATOR=Arbitrator
# suppress inspection "UnusedProperty"

View File

@ -1143,9 +1143,9 @@ dao.bonding.dashboard.lockupAmount=Lockup funds:
dao.bonding.dashboard.unlockingAmount=Unlocking funds (wait until lock time is over):
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Bonded role
dao.bond.lockupReason.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Bonded reputation
dao.bond.lockupReason.REPUTATION=Bonded reputation
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.ARBITRATOR=Arbitrator
# suppress inspection "UnusedProperty"

View File

@ -1143,9 +1143,9 @@ dao.bonding.dashboard.lockupAmount=Lockup funds:
dao.bonding.dashboard.unlockingAmount=Unlocking funds (wait until lock time is over):
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Bonded role
dao.bond.lockupReason.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Bonded reputation
dao.bond.lockupReason.REPUTATION=Bonded reputation
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.ARBITRATOR=Arbitrator
# suppress inspection "UnusedProperty"

View File

@ -1143,9 +1143,9 @@ dao.bonding.dashboard.lockupAmount=Lockup funds:
dao.bonding.dashboard.unlockingAmount=Unlocking funds (wait until lock time is over):
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Bonded role
dao.bond.lockupReason.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Bonded reputation
dao.bond.lockupReason.REPUTATION=Bonded reputation
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.ARBITRATOR=Arbitrator
# suppress inspection "UnusedProperty"

View File

@ -1143,9 +1143,9 @@ dao.bonding.dashboard.lockupAmount=Запереть средства:
dao.bonding.dashboard.unlockingAmount=Разблокировка средств (дождитесь окончания времени блокировки):
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Обеспеченная роль
dao.bond.lockupReason.BONDED_ROLE=Обеспеченная роль
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Обеспеченная репутация
dao.bond.lockupReason.REPUTATION=Обеспеченная репутация
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.ARBITRATOR=Арбитр
# suppress inspection "UnusedProperty"

View File

@ -1143,9 +1143,9 @@ dao.bonding.dashboard.lockupAmount=Lockup funds:
dao.bonding.dashboard.unlockingAmount=Unlocking funds (wait until lock time is over):
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Bonded role
dao.bond.lockupReason.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Bonded reputation
dao.bond.lockupReason.REPUTATION=Bonded reputation
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.ARBITRATOR=Arbitrator
# suppress inspection "UnusedProperty"

View File

@ -1143,9 +1143,9 @@ dao.bonding.dashboard.lockupAmount=Lockup funds:
dao.bonding.dashboard.unlockingAmount=Unlocking funds (wait until lock time is over):
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Bonded role
dao.bond.lockupReason.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Bonded reputation
dao.bond.lockupReason.REPUTATION=Bonded reputation
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.ARBITRATOR=Arbitrator
# suppress inspection "UnusedProperty"

View File

@ -1143,9 +1143,9 @@ dao.bonding.dashboard.lockupAmount=Lockup funds:
dao.bonding.dashboard.unlockingAmount=Unlocking funds (wait until lock time is over):
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Bonded role
dao.bond.lockupReason.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Bonded reputation
dao.bond.lockupReason.REPUTATION=Bonded reputation
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.ARBITRATOR=Arbitrator
# suppress inspection "UnusedProperty"

View File

@ -1143,9 +1143,9 @@ dao.bonding.dashboard.lockupAmount=Lockup funds:
dao.bonding.dashboard.unlockingAmount=Unlocking funds (wait until lock time is over):
# suppress inspection "UnusedProperty"
dao.bond.lockupType.BONDED_ROLE=Bonded role
dao.bond.lockupReason.BONDED_ROLE=Bonded role
# suppress inspection "UnusedProperty"
dao.bond.lockupType.REPUTATION=Bonded reputation
dao.bond.lockupReason.REPUTATION=Bonded reputation
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleType.ARBITRATOR=Arbitrator
# suppress inspection "UnusedProperty"

View File

@ -26,10 +26,10 @@ import bisq.desktop.util.GUIUtil;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.governance.bond.lockup.LockupType;
import bisq.core.dao.governance.bond.lockup.LockupReason;
import bisq.core.dao.governance.bond.reputation.MyReputation;
import bisq.core.dao.governance.bond.reputation.MyReputationListService;
import bisq.core.dao.governance.bond.role.BondedRolesService;
import bisq.core.dao.governance.bond.role.BondedRolesRepository;
import bisq.core.dao.state.model.blockchain.TxOutput;
import bisq.core.dao.state.model.governance.BondedRoleType;
import bisq.core.dao.state.model.governance.Role;
@ -56,7 +56,7 @@ import static com.google.common.base.Preconditions.checkArgument;
public class BondingViewUtils {
private final P2PService p2PService;
private final MyReputationListService myReputationListService;
private final BondedRolesService bondedRolesService;
private final BondedRolesRepository bondedRolesRepository;
private final WalletsSetup walletsSetup;
private final DaoFacade daoFacade;
private final Navigation navigation;
@ -65,14 +65,14 @@ public class BondingViewUtils {
@Inject
public BondingViewUtils(P2PService p2PService,
MyReputationListService myReputationListService,
BondedRolesService bondedRolesService,
BondedRolesRepository bondedRolesRepository,
WalletsSetup walletsSetup,
DaoFacade daoFacade,
Navigation navigation,
BsqFormatter bsqFormatter) {
this.p2PService = p2PService;
this.myReputationListService = myReputationListService;
this.bondedRolesService = bondedRolesService;
this.bondedRolesRepository = bondedRolesRepository;
this.walletsSetup = walletsSetup;
this.daoFacade = daoFacade;
this.navigation = navigation;
@ -83,8 +83,8 @@ public class BondingViewUtils {
BondedRoleType bondedRoleType = role.getBondedRoleType();
Coin lockupAmount = Coin.valueOf(bondedRoleType.getRequiredBond());
int lockupTime = bondedRoleType.getUnlockTimeInBlocks();
if (!bondedRolesService.wasBondedAssetAlreadyBonded(role)) {
lockupBond(role.getHash(), lockupAmount, lockupTime, LockupType.BONDED_ROLE, resultHandler);
if (!bondedRolesRepository.isBondedAssetAlreadyInBond(role)) {
lockupBond(role.getHash(), lockupAmount, lockupTime, LockupReason.BONDED_ROLE, resultHandler);
} else {
handleError(new RuntimeException("The role has been used already for a lockup tx."));
}
@ -92,11 +92,11 @@ public class BondingViewUtils {
public void lockupBondForReputation(Coin lockupAmount, int lockupTime, byte[] salt, Consumer<String> resultHandler) {
MyReputation myReputation = new MyReputation(salt);
lockupBond(myReputation.getHash(), lockupAmount, lockupTime, LockupType.REPUTATION, resultHandler);
lockupBond(myReputation.getHash(), lockupAmount, lockupTime, LockupReason.REPUTATION, resultHandler);
myReputationListService.addReputation(myReputation);
}
private void lockupBond(byte[] hash, Coin lockupAmount, int lockupTime, LockupType lockupType,
private void lockupBond(byte[] hash, Coin lockupAmount, int lockupTime, LockupReason lockupReason,
Consumer<String> resultHandler) {
if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) {
if (!DevEnv.isDevMode()) {
@ -106,21 +106,21 @@ public class BondingViewUtils {
lockupTime
))
.actionButtonText(Res.get("shared.yes"))
.onAction(() -> publishLockupTx(hash, lockupAmount, lockupTime, lockupType, resultHandler))
.onAction(() -> publishLockupTx(hash, lockupAmount, lockupTime, lockupReason, resultHandler))
.closeButtonText(Res.get("shared.cancel"))
.show();
} else {
publishLockupTx(hash, lockupAmount, lockupTime, lockupType, resultHandler);
publishLockupTx(hash, lockupAmount, lockupTime, lockupReason, resultHandler);
}
} else {
GUIUtil.showNotReadyForTxBroadcastPopups(p2PService, walletsSetup);
}
}
private void publishLockupTx(byte[] hash, Coin lockupAmount, int lockupTime, LockupType lockupType, Consumer<String> resultHandler) {
private void publishLockupTx(byte[] hash, Coin lockupAmount, int lockupTime, LockupReason lockupReason, Consumer<String> resultHandler) {
daoFacade.publishLockupTx(lockupAmount,
lockupTime,
lockupType,
lockupReason,
hash,
txId -> {
if (!DevEnv.isDevMode())

View File

@ -22,7 +22,7 @@ import bisq.desktop.main.dao.bonding.BondingViewUtils;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.governance.bond.Bond;
import bisq.core.dao.governance.bond.role.BondedRole;
import bisq.core.dao.governance.bond.role.BondedRolesService;
import bisq.core.dao.governance.bond.role.BondedRolesRepository;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.locale.Res;
@ -48,7 +48,7 @@ import lombok.extern.slf4j.Slf4j;
class BondListItem implements DaoStateListener {
private final Bond bond;
private final DaoFacade daoFacade;
private final BondedRolesService bondedRolesService;
private final BondedRolesRepository bondedRolesRepository;
private final BondingViewUtils bondingViewUtils;
private final BsqFormatter bsqFormatter;
private final String bondType;
@ -64,12 +64,12 @@ class BondListItem implements DaoStateListener {
BondListItem(Bond bond,
DaoFacade daoFacade,
BondedRolesService bondedRolesService,
BondedRolesRepository bondedRolesRepository,
BondingViewUtils bondingViewUtils,
BsqFormatter bsqFormatter) {
this.bond = bond;
this.daoFacade = daoFacade;
this.bondedRolesService = bondedRolesService;
this.bondedRolesRepository = bondedRolesRepository;
this.bondingViewUtils = bondingViewUtils;
this.bsqFormatter = bsqFormatter;

View File

@ -28,7 +28,7 @@ import bisq.desktop.util.validation.BsqValidator;
import bisq.core.btc.listeners.BsqBalanceListener;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.governance.bond.role.BondedRolesService;
import bisq.core.dao.governance.bond.role.BondedRolesRepository;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.locale.Res;
@ -75,7 +75,7 @@ public class BondsView extends ActivatableView<GridPane, Void> implements BsqBal
private final BsqFormatter bsqFormatter;
private final BsqValidator bsqValidator;
private final BondingViewUtils bondingViewUtils;
private final BondedRolesService bondedRolesService;
private final BondedRolesRepository bondedRolesRepository;
private final DaoFacade daoFacade;
private final Preferences preferences;
@ -97,14 +97,14 @@ public class BondsView extends ActivatableView<GridPane, Void> implements BsqBal
BsqFormatter bsqFormatter,
BsqValidator bsqValidator,
BondingViewUtils bondingViewUtils,
BondedRolesService bondedRolesService,
BondedRolesRepository bondedRolesRepository,
DaoFacade daoFacade,
Preferences preferences) {
this.bsqWalletService = bsqWalletService;
this.bsqFormatter = bsqFormatter;
this.bsqValidator = bsqValidator;
this.bondingViewUtils = bondingViewUtils;
this.bondedRolesService = bondedRolesService;
this.bondedRolesRepository = bondedRolesRepository;
this.daoFacade = daoFacade;
this.preferences = preferences;
}
@ -208,7 +208,7 @@ public class BondsView extends ActivatableView<GridPane, Void> implements BsqBal
.map(bond -> {
return new BondListItem(bond,
daoFacade,
bondedRolesService,
bondedRolesRepository,
bondingViewUtils,
bsqFormatter);
})