Complete handling of bonds for roles.

- Add BondedRoleState to reflect all diff. states.
- Show in UI correct state and button state for lockup/revoke
- Rename ProposalService classes to ProposalFactory
This commit is contained in:
Manfred Karrer 2018-11-02 16:09:53 -05:00
parent 10b14a69cc
commit 8ce8813e89
No known key found for this signature in database
GPG key ID: 401250966A6B2C46
34 changed files with 321 additions and 249 deletions

View file

@ -42,14 +42,14 @@ import bisq.core.dao.governance.proposal.ProposalListPresentation;
import bisq.core.dao.governance.proposal.ProposalWithTransaction;
import bisq.core.dao.governance.proposal.TxException;
import bisq.core.dao.governance.proposal.compensation.CompensationConsensus;
import bisq.core.dao.governance.proposal.compensation.CompensationProposalService;
import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposalService;
import bisq.core.dao.governance.proposal.generic.GenericProposalService;
import bisq.core.dao.governance.proposal.param.ChangeParamProposalService;
import bisq.core.dao.governance.proposal.compensation.CompensationProposalFactory;
import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposalFactory;
import bisq.core.dao.governance.proposal.generic.GenericProposalFactory;
import bisq.core.dao.governance.proposal.param.ChangeParamProposalFactory;
import bisq.core.dao.governance.proposal.reimbursement.ReimbursementConsensus;
import bisq.core.dao.governance.proposal.reimbursement.ReimbursementProposalService;
import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetProposalService;
import bisq.core.dao.governance.proposal.role.RoleProposalService;
import bisq.core.dao.governance.proposal.reimbursement.ReimbursementProposalFactory;
import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetProposalFactory;
import bisq.core.dao.governance.proposal.role.RoleProposalFactory;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.DaoStateStorageService;
@ -89,6 +89,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import lombok.extern.slf4j.Slf4j;
@ -108,13 +109,13 @@ public class DaoFacade implements DaoSetupService {
private final PeriodService periodService;
private final MyBlindVoteListService myBlindVoteListService;
private final MyVoteListService myVoteListService;
private final CompensationProposalService compensationProposalService;
private final ReimbursementProposalService reimbursementProposalService;
private final ChangeParamProposalService changeParamProposalService;
private final ConfiscateBondProposalService confiscateBondProposalService;
private final RoleProposalService roleProposalService;
private final GenericProposalService genericProposalService;
private final RemoveAssetProposalService removeAssetProposalService;
private final CompensationProposalFactory compensationProposalFactory;
private final ReimbursementProposalFactory reimbursementProposalFactory;
private final ChangeParamProposalFactory changeParamProposalFactory;
private final ConfiscateBondProposalFactory confiscateBondProposalFactory;
private final RoleProposalFactory roleProposalFactory;
private final GenericProposalFactory genericProposalFactory;
private final RemoveAssetProposalFactory removeAssetProposalFactory;
private final BondedRolesService bondedRolesService;
private final BondedReputationService bondedReputationService;
private final LockupService lockupService;
@ -132,13 +133,13 @@ public class DaoFacade implements DaoSetupService {
PeriodService periodService,
MyBlindVoteListService myBlindVoteListService,
MyVoteListService myVoteListService,
CompensationProposalService compensationProposalService,
ReimbursementProposalService reimbursementProposalService,
ChangeParamProposalService changeParamProposalService,
ConfiscateBondProposalService confiscateBondProposalService,
RoleProposalService roleProposalService,
GenericProposalService genericProposalService,
RemoveAssetProposalService removeAssetProposalService,
CompensationProposalFactory compensationProposalFactory,
ReimbursementProposalFactory reimbursementProposalFactory,
ChangeParamProposalFactory changeParamProposalFactory,
ConfiscateBondProposalFactory confiscateBondProposalFactory,
RoleProposalFactory roleProposalFactory,
GenericProposalFactory genericProposalFactory,
RemoveAssetProposalFactory removeAssetProposalFactory,
BondedRolesService bondedRolesService,
BondedReputationService bondedReputationService,
LockupService lockupService,
@ -152,13 +153,13 @@ public class DaoFacade implements DaoSetupService {
this.periodService = periodService;
this.myBlindVoteListService = myBlindVoteListService;
this.myVoteListService = myVoteListService;
this.compensationProposalService = compensationProposalService;
this.reimbursementProposalService = reimbursementProposalService;
this.changeParamProposalService = changeParamProposalService;
this.confiscateBondProposalService = confiscateBondProposalService;
this.roleProposalService = roleProposalService;
this.genericProposalService = genericProposalService;
this.removeAssetProposalService = removeAssetProposalService;
this.compensationProposalFactory = compensationProposalFactory;
this.reimbursementProposalFactory = reimbursementProposalFactory;
this.changeParamProposalFactory = changeParamProposalFactory;
this.confiscateBondProposalFactory = confiscateBondProposalFactory;
this.roleProposalFactory = roleProposalFactory;
this.genericProposalFactory = genericProposalFactory;
this.removeAssetProposalFactory = removeAssetProposalFactory;
this.bondedRolesService = bondedRolesService;
this.bondedReputationService = bondedReputationService;
this.lockupService = lockupService;
@ -229,7 +230,7 @@ public class DaoFacade implements DaoSetupService {
String link,
Coin requestedBsq)
throws ValidationException, InsufficientMoneyException, TxException {
return compensationProposalService.createProposalWithTransaction(name,
return compensationProposalFactory.createProposalWithTransaction(name,
link,
requestedBsq);
}
@ -238,7 +239,7 @@ public class DaoFacade implements DaoSetupService {
String link,
Coin requestedBsq)
throws ValidationException, InsufficientMoneyException, TxException {
return reimbursementProposalService.createProposalWithTransaction(name,
return reimbursementProposalFactory.createProposalWithTransaction(name,
link,
requestedBsq);
}
@ -248,7 +249,7 @@ public class DaoFacade implements DaoSetupService {
Param param,
String paramValue)
throws ValidationException, InsufficientMoneyException, TxException {
return changeParamProposalService.createProposalWithTransaction(name,
return changeParamProposalFactory.createProposalWithTransaction(name,
link,
param,
paramValue);
@ -258,31 +259,31 @@ public class DaoFacade implements DaoSetupService {
String link,
byte[] hash)
throws ValidationException, InsufficientMoneyException, TxException {
return confiscateBondProposalService.createProposalWithTransaction(name,
return confiscateBondProposalFactory.createProposalWithTransaction(name,
link,
hash);
}
public ProposalWithTransaction getBondedRoleProposalWithTransaction(Role role)
throws ValidationException, InsufficientMoneyException, TxException {
return roleProposalService.createProposalWithTransaction(role);
return roleProposalFactory.createProposalWithTransaction(role);
}
public ProposalWithTransaction getGenericProposalWithTransaction(String name,
String link)
throws ValidationException, InsufficientMoneyException, TxException {
return genericProposalService.createProposalWithTransaction(name, link);
return genericProposalFactory.createProposalWithTransaction(name, link);
}
public ProposalWithTransaction getRemoveAssetProposalWithTransaction(String name,
String link,
Asset asset)
throws ValidationException, InsufficientMoneyException, TxException {
return removeAssetProposalService.createProposalWithTransaction(name, link, asset);
return removeAssetProposalFactory.createProposalWithTransaction(name, link, asset);
}
public Collection<BondedRole> getBondedRoleStates() {
return bondedRolesService.getBondedRoleStates();
public Collection<BondedRole> getBondedRoles() {
return bondedRolesService.getBondedRoles();
}
public List<BondedReputation> getBondedReputationList() {
@ -487,11 +488,11 @@ public class DaoFacade implements DaoSetupService {
///////////////////////////////////////////////////////////////////////////////////////////
public void publishLockupTx(Coin lockupAmount, int lockTime, LockupType lockupType, BondWithHash bondWithHash,
ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
Consumer<String> resultHandler, ExceptionHandler exceptionHandler) {
lockupService.publishLockupTx(lockupAmount, lockTime, lockupType, bondWithHash, resultHandler, exceptionHandler);
}
public void publishUnlockTx(String lockupTxId, ResultHandler resultHandler,
public void publishUnlockTx(String lockupTxId, Consumer<String> resultHandler,
ExceptionHandler exceptionHandler) {
unlockService.publishUnlockTx(lockupTxId, resultHandler, exceptionHandler);
}
@ -656,4 +657,8 @@ public class DaoFacade implements DaoSetupService {
public void resyncDao(Runnable resultHandler) {
daoStateStorageService.resetDaoState(resultHandler);
}
public boolean isMyRole(Role role) {
return bondedRolesService.isMyRole(role);
}
}

View file

@ -37,19 +37,19 @@ import bisq.core.dao.governance.proposal.MyProposalListService;
import bisq.core.dao.governance.proposal.ProposalListPresentation;
import bisq.core.dao.governance.proposal.ProposalService;
import bisq.core.dao.governance.proposal.ProposalValidator;
import bisq.core.dao.governance.proposal.compensation.CompensationProposalService;
import bisq.core.dao.governance.proposal.compensation.CompensationProposalFactory;
import bisq.core.dao.governance.proposal.compensation.CompensationValidator;
import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposalService;
import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondProposalFactory;
import bisq.core.dao.governance.proposal.confiscatebond.ConfiscateBondValidator;
import bisq.core.dao.governance.proposal.generic.GenericProposalService;
import bisq.core.dao.governance.proposal.generic.GenericProposalFactory;
import bisq.core.dao.governance.proposal.generic.GenericProposalValidator;
import bisq.core.dao.governance.proposal.param.ChangeParamProposalService;
import bisq.core.dao.governance.proposal.param.ChangeParamProposalFactory;
import bisq.core.dao.governance.proposal.param.ChangeParamValidator;
import bisq.core.dao.governance.proposal.reimbursement.ReimbursementProposalService;
import bisq.core.dao.governance.proposal.reimbursement.ReimbursementProposalFactory;
import bisq.core.dao.governance.proposal.reimbursement.ReimbursementValidator;
import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetProposalService;
import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetProposalFactory;
import bisq.core.dao.governance.proposal.removeAsset.RemoveAssetValidator;
import bisq.core.dao.governance.proposal.role.RoleProposalService;
import bisq.core.dao.governance.proposal.role.RoleProposalFactory;
import bisq.core.dao.governance.proposal.role.RoleValidator;
import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStorageService;
import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStore;
@ -132,25 +132,25 @@ public class DaoModule extends AppModule {
bind(ProposalValidator.class).in(Singleton.class);
bind(CompensationValidator.class).in(Singleton.class);
bind(CompensationProposalService.class).in(Singleton.class);
bind(CompensationProposalFactory.class).in(Singleton.class);
bind(ReimbursementValidator.class).in(Singleton.class);
bind(ReimbursementProposalService.class).in(Singleton.class);
bind(ReimbursementProposalFactory.class).in(Singleton.class);
bind(ChangeParamValidator.class).in(Singleton.class);
bind(ChangeParamProposalService.class).in(Singleton.class);
bind(ChangeParamProposalFactory.class).in(Singleton.class);
bind(RoleValidator.class).in(Singleton.class);
bind(RoleProposalService.class).in(Singleton.class);
bind(RoleProposalFactory.class).in(Singleton.class);
bind(ConfiscateBondValidator.class).in(Singleton.class);
bind(ConfiscateBondProposalService.class).in(Singleton.class);
bind(ConfiscateBondProposalFactory.class).in(Singleton.class);
bind(GenericProposalValidator.class).in(Singleton.class);
bind(GenericProposalService.class).in(Singleton.class);
bind(GenericProposalFactory.class).in(Singleton.class);
bind(RemoveAssetValidator.class).in(Singleton.class);
bind(RemoveAssetProposalService.class).in(Singleton.class);
bind(RemoveAssetProposalFactory.class).in(Singleton.class);
// Ballot

View file

@ -30,32 +30,20 @@ import javax.annotation.Nullable;
@Getter
public class BondedRole {
private final Role role;
@Setter
private long startDate;
// LockupTxId is null as long the bond holder has not been accepted by voting and made the lockup tx.
// It will get set after the proposal has been accepted and the lockup tx is confirmed.
@Setter
@Nullable
private String lockupTxId;
// Date when role has been revoked
@Setter
private long revokeDate;
@Setter
@Nullable
private String unlockTxId;
@Setter
private boolean isUnlocking;
private long startDate;
@Setter
private long revokeDate;
@Setter
private BondedRoleState bondedRoleState = BondedRoleState.READY_FOR_LOCKUP;
BondedRole(Role role) {
this.role = role;
}
public boolean isLockedUp() {
return lockupTxId != null;
}
public boolean isUnlocked() {
return unlockTxId != null;
}
}

View file

@ -0,0 +1,29 @@
/*
* 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;
// Used in string properties ("dao.bond.bondedRoleState.*")
public enum BondedRoleState {
READY_FOR_LOCKUP, // Accepted by voting 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,
}

View file

@ -17,7 +17,9 @@
package bisq.core.dao.governance.bond;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.governance.bonding.BondingConsensus;
import bisq.core.dao.governance.bonding.bond.BondWithHash;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.BaseTxOutput;
@ -25,9 +27,15 @@ import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.blockchain.SpentInfo;
import bisq.core.dao.state.model.blockchain.TxType;
import bisq.core.dao.state.model.governance.BondedRoleType;
import bisq.core.dao.state.model.governance.Proposal;
import bisq.core.dao.state.model.governance.Role;
import bisq.core.dao.state.model.governance.RoleProposal;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.TransactionInput;
import org.bitcoinj.core.TransactionOutput;
import javax.inject.Inject;
import java.util.Arrays;
@ -35,7 +43,9 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -49,9 +59,10 @@ import static com.google.common.base.Preconditions.checkArgument;
@Slf4j
public class BondedRolesService implements DaoStateListener {
private final DaoStateService daoStateService;
private final BsqWalletService bsqWalletService;
// This map is just for convenience. The data which are used to fill the map are store din the DaoState (role, txs).
private final Map<String, BondedRole> bondedRoleStateMap = new HashMap<>();
private final Map<String, BondedRole> bondedRoleByRoleUidMap = new HashMap<>();
///////////////////////////////////////////////////////////////////////////////////////////
@ -59,8 +70,9 @@ public class BondedRolesService implements DaoStateListener {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public BondedRolesService(DaoStateService daoStateService) {
public BondedRolesService(DaoStateService daoStateService, BsqWalletService bsqWalletService) {
this.daoStateService = daoStateService;
this.bsqWalletService = bsqWalletService;
daoStateService.addBsqStateListener(this);
}
@ -78,8 +90,8 @@ public class BondedRolesService implements DaoStateListener {
public void onParseTxsComplete(Block block) {
// TODO optimize to not re-write the whole map at each block
getBondedRoleStream().forEach(bondedRole -> {
bondedRoleStateMap.putIfAbsent(bondedRole.getUid(), new BondedRole(bondedRole));
BondedRole bondedRoleState = bondedRoleStateMap.get(bondedRole.getUid());
bondedRoleByRoleUidMap.putIfAbsent(bondedRole.getUid(), new BondedRole(bondedRole));
BondedRole bondedRoleState = bondedRoleByRoleUidMap.get(bondedRole.getUid());
// Lets see if we have a lock up tx.
daoStateService.getLockupTxOutputs().forEach(lockupTxOutput -> {
@ -94,17 +106,10 @@ public class BondedRolesService implements DaoStateListener {
byte[] hash = BondingConsensus.getHashFromOpReturnData(opReturnData);
Optional<Role> candidate = getBondedRoleFromHash(hash);
if (candidate.isPresent() && bondedRole.equals(candidate.get())) {
if (bondedRoleState.getLockupTxId() == null) {
bondedRoleState.setLockupTxId(lockupTxId);
// We use the tx time as we want to have a unique time for all users
bondedRoleState.setStartDate(lockupTx.getTime());
} else {
checkArgument(bondedRoleState.getLockupTxId().equals(lockupTxId),
"We have already the lockup tx set in bondedRoleState " +
"but it is different to the one we found in the daoState transactions. " +
"That should not happen. bondedRole={}, bondedRoleState={}",
bondedRole, bondedRoleState);
}
bondedRoleState.setBondedRoleState(BondedRoleState.LOCKUP_TX_CONFIRMED);
bondedRoleState.setLockupTxId(lockupTxId);
// We use the tx time as we want to have a unique time for all users
bondedRoleState.setStartDate(lockupTx.getTime());
if (!daoStateService.isUnspent(lockupTxOutput.getKey())) {
// Lockup is already spent (in unlock tx)
@ -115,26 +120,24 @@ public class BondedRolesService implements DaoStateListener {
.ifPresent(unlockTx -> {
// cross check if it is in daoStateService.getUnlockTxOutputs() ?
String unlockTxId = unlockTx.getId();
if (bondedRoleState.getUnlockTxId() == null) {
bondedRoleState.setUnlockTxId(unlockTxId);
bondedRoleState.setRevokeDate(unlockTx.getTime());
bondedRoleState.setUnlocking(daoStateService.isUnlocking(unlockTxId));
//TODO after locktime set to false or maybe better use states for lock state
bondedRoleState.setUnlockTxId(unlockTxId);
bondedRoleState.setBondedRoleState(BondedRoleState.UNLOCK_TX_CONFIRMED);
bondedRoleState.setRevokeDate(unlockTx.getTime());
boolean unlocking = daoStateService.isUnlocking(unlockTxId);
if (unlocking) {
bondedRoleState.setBondedRoleState(BondedRoleState.UNLOCKING);
} else {
checkArgument(bondedRoleState.getUnlockTxId().equals(unlockTxId),
"We have already the unlock tx set in bondedRoleState " +
"but it is different to the one we found in the daoState transactions. " +
"That should not happen. bondedRole={}, bondedRoleState={}",
bondedRole, bondedRoleState);
bondedRoleState.setBondedRoleState(BondedRoleState.UNLOCKED);
}
// TODO check lock time
});
}
}
});
});
});
updateBondedRoleStateFromUnconfirmedLockupTxs();
updateBondedRoleStateFromUnconfirmedUnlockTxs();
}
@ -155,8 +158,8 @@ public class BondedRolesService implements DaoStateListener {
return getBondedRoleStream().collect(Collectors.toList());
}
public Collection<BondedRole> getBondedRoleStates() {
return bondedRoleStateMap.values();
public Collection<BondedRole> getBondedRoles() {
return bondedRoleByRoleUidMap.values();
}
// bonded roles which are active and can be confiscated
@ -165,6 +168,46 @@ public class BondedRolesService implements DaoStateListener {
return getBondedRoleList();
}
private void updateBondedRoleStateFromUnconfirmedLockupTxs() {
getBondedRoleStream().filter(this::isLockupTxUnconfirmed)
.map(role -> bondedRoleByRoleUidMap.get(role.getUid()))
.filter(bondedRole -> bondedRole.getBondedRoleState() == BondedRoleState.READY_FOR_LOCKUP)
.forEach(bondedRole -> bondedRole.setBondedRoleState(BondedRoleState.LOCKUP_TX_PENDING));
}
private void updateBondedRoleStateFromUnconfirmedUnlockTxs() {
getBondedRoleStream().filter(this::isUnlockTxUnconfirmed)
.map(role -> bondedRoleByRoleUidMap.get(role.getUid()))
.filter(bondedRole -> bondedRole.getBondedRoleState() == BondedRoleState.LOCKUP_TX_CONFIRMED)
.forEach(bondedRole -> bondedRole.setBondedRoleState(BondedRoleState.UNLOCK_TX_PENDING));
}
private boolean isLockupTxUnconfirmed(BondWithHash bondWithHash) {
return bsqWalletService.getWalletTransactions().stream()
.filter(transaction -> transaction.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.PENDING)
.map(transaction -> transaction.getOutputs().get(transaction.getOutputs().size() - 1))
.filter(lastOutput -> lastOutput.getScriptPubKey().isOpReturn())
.map(lastOutput -> lastOutput.getScriptPubKey().getChunks())
.filter(chunks -> chunks.size() > 1)
.map(chunks -> chunks.get(1).data)
.anyMatch(data -> Arrays.equals(BondingConsensus.getHashFromOpReturnData(data), bondWithHash.getHash()));
}
private boolean isUnlockTxUnconfirmed(BondWithHash bondWithHash) {
return bsqWalletService.getWalletTransactions().stream()
.filter(transaction -> transaction.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.PENDING)
.filter(transaction -> transaction.getInputs().size() > 1)
.map(transaction -> transaction.getInputs().get(0))
.map(TransactionInput::getConnectedOutput)
.filter(Objects::nonNull)
.map(TransactionOutput::getParentTransaction)
.filter(Objects::nonNull)
.map(Transaction::getHashAsString)
.flatMap(lockupTxId -> daoStateService.getLockupOpReturnTxOutput(lockupTxId).stream())
.map(BaseTxOutput::getOpReturnData)
.anyMatch(data -> Arrays.equals(BondingConsensus.getHashFromOpReturnData(data), bondWithHash.getHash()));
}
public Optional<Role> getBondedRoleFromHash(byte[] hash) {
return getBondedRoleStream()
.filter(bondedRole -> {
@ -180,7 +223,7 @@ public class BondedRolesService implements DaoStateListener {
}
public Optional<BondedRole> getBondedRoleStateFromLockupTxId(String lockupTxId) {
return bondedRoleStateMap.values().stream()
return bondedRoleByRoleUidMap.values().stream()
.filter(bondedRoleState -> lockupTxId.equals(bondedRoleState.getLockupTxId()))
.findAny();
}
@ -204,32 +247,25 @@ public class BondedRolesService implements DaoStateListener {
.map(e -> ((RoleProposal) e.getProposal()).getRole());
}
private Optional<byte[]> getOpReturnData(String lockUpTxId) {
return daoStateService.getLockupOpReturnTxOutput(lockUpTxId).map(BaseTxOutput::getOpReturnData);
private Stream<RoleProposal> getBondedRoleProposalStream() {
return daoStateService.getEvaluatedProposalList().stream()
.filter(evaluatedProposal -> evaluatedProposal.getProposal() instanceof RoleProposal)
.map(e -> ((RoleProposal) e.getProposal()));
}
public boolean wasRoleAlreadyBonded(Role role) {
BondedRole bondedRole = bondedRoleStateMap.get(role.getUid());
return bondedRole != null && bondedRole.getLockupTxId() != null;
BondedRole bondedRole = bondedRoleByRoleUidMap.get(role.getUid());
checkArgument(bondedRole != null, "bondedRole must not be null");
return bondedRole.getLockupTxId() != null;
}
/* private Optional<LockupType> getOptionalLockupType(String lockUpTxId) {
return getOpReturnData(lockUpTxId)
.flatMap(BondingConsensus::getLockupType);
}*/
/*public static Optional<Role> getBondedRoleByLockupTxId(String lockupTxId) {
return bondedRoles.stream()
.filter(bondedRole -> bondedRole.getLockupTxId().equals(lockupTxId)).
findAny();
}*/
/*
public static Optional<Role> getBondedRoleByHashOfBondId(byte[] hash) {
return Optional.empty();
*//* bondedRoles.stream()
.filter(bondedRole -> Arrays.equals(bondedRole.getHash(), hash))
.findAny();*//*
}*/
public boolean isMyRole(Role role) {
Set<String> myWalletTransactionIds = bsqWalletService.getWalletTransactions().stream()
.map(Transaction::getHashAsString)
.collect(Collectors.toSet());
return getBondedRoleProposalStream()
.filter(roleProposal -> roleProposal.getRole().equals(role))
.map(Proposal::getTxId)
.anyMatch(myWalletTransactionIds::contains);
}
}

View file

@ -31,7 +31,6 @@ import bisq.core.dao.governance.bonding.bond.BondWithHash;
import bisq.core.dao.state.model.governance.Role;
import bisq.common.handlers.ExceptionHandler;
import bisq.common.handlers.ResultHandler;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
@ -41,6 +40,8 @@ import javax.inject.Inject;
import java.io.IOException;
import java.util.function.Consumer;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
@ -69,10 +70,9 @@ public class LockupService {
}
public void publishLockupTx(Coin lockupAmount, int lockTime, LockupType lockupType, BondWithHash bondWithHash,
ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
Consumer<String> resultHandler, ExceptionHandler exceptionHandler) {
checkArgument(lockTime <= BondingConsensus.getMaxLockTime() &&
lockTime >= BondingConsensus.getMinLockTime(), "lockTime not in rage");
if (bondWithHash instanceof Role) {
Role role = (Role) bondWithHash;
if (bondedRolesService.wasRoleAlreadyBonded(role)) {
@ -81,17 +81,16 @@ public class LockupService {
}
}
byte[] hash = BondingConsensus.getHash(bondWithHash);
try {
byte[] hash = BondingConsensus.getHash(bondWithHash);
byte[] opReturnData = BondingConsensus.getLockupOpReturnData(lockTime, lockupType, hash);
final Transaction lockupTx = getLockupTx(lockupAmount, opReturnData);
final Transaction lockupTx = createLockupTx(lockupAmount, opReturnData);
//noinspection Duplicates
walletsManager.publishAndCommitBsqTx(lockupTx, new TxBroadcaster.Callback() {
@Override
public void onSuccess(Transaction transaction) {
resultHandler.handleResult();
resultHandler.accept(transaction.getHashAsString());
}
@Override
@ -111,7 +110,7 @@ public class LockupService {
}
}
private Transaction getLockupTx(Coin lockupAmount, byte[] opReturnData)
private Transaction createLockupTx(Coin lockupAmount, byte[] opReturnData)
throws InsufficientMoneyException, WalletException, TransactionVerificationException {
Transaction preparedTx = bsqWalletService.getPreparedLockupTx(lockupAmount);
Transaction txWithBtcFee = btcWalletService.completePreparedBsqTx(preparedTx, true, opReturnData);

View file

@ -29,13 +29,14 @@ import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.TxOutput;
import bisq.common.handlers.ExceptionHandler;
import bisq.common.handlers.ResultHandler;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.Transaction;
import javax.inject.Inject;
import java.util.function.Consumer;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@ -61,7 +62,7 @@ public class UnlockService {
this.daoStateService = daoStateService;
}
public void publishUnlockTx(String lockupTxId, ResultHandler resultHandler,
public void publishUnlockTx(String lockupTxId, Consumer<String> resultHandler,
ExceptionHandler exceptionHandler) {
try {
TxOutput lockupTxOutput = daoStateService.getLockupTxOutput(lockupTxId).get();
@ -71,7 +72,7 @@ public class UnlockService {
walletsManager.publishAndCommitBsqTx(unlockTx, new TxBroadcaster.Callback() {
@Override
public void onSuccess(Transaction transaction) {
resultHandler.handleResult();
resultHandler.accept(transaction.getHashAsString());
}
@Override

View file

@ -37,10 +37,11 @@ import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
/**
* Base class for proposalService classes. Provides creation of a transaction.
* Base class for proposalFactory classes. Provides creation of a transaction. Proposal creation is delegated to
* concrete classes.
*/
@Slf4j
public abstract class BaseProposalService<R extends Proposal> {
public abstract class BaseProposalFactory<R extends Proposal> {
protected final BsqWalletService bsqWalletService;
protected final BtcWalletService btcWalletService;
protected final DaoStateService daoStateService;
@ -55,7 +56,7 @@ public abstract class BaseProposalService<R extends Proposal> {
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public BaseProposalService(BsqWalletService bsqWalletService,
public BaseProposalFactory(BsqWalletService bsqWalletService,
BtcWalletService btcWalletService,
DaoStateService daoStateService,
ProposalValidator proposalValidator) {

View file

@ -22,7 +22,7 @@ import bisq.core.btc.exceptions.WalletException;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.dao.exceptions.ValidationException;
import bisq.core.dao.governance.proposal.BaseProposalService;
import bisq.core.dao.governance.proposal.BaseProposalFactory;
import bisq.core.dao.governance.proposal.ProposalConsensus;
import bisq.core.dao.governance.proposal.ProposalWithTransaction;
import bisq.core.dao.governance.proposal.TxException;
@ -45,13 +45,13 @@ import lombok.extern.slf4j.Slf4j;
* Creates the CompensationProposal and the transaction.
*/
@Slf4j
public class CompensationProposalService extends BaseProposalService<CompensationProposal> {
public class CompensationProposalFactory extends BaseProposalFactory<CompensationProposal> {
private Coin requestedBsq;
private String bsqAddress;
@Inject
public CompensationProposalService(BsqWalletService bsqWalletService,
public CompensationProposalFactory(BsqWalletService bsqWalletService,
BtcWalletService btcWalletService,
DaoStateService daoStateService,
CompensationValidator proposalValidator) {

View file

@ -20,7 +20,7 @@ package bisq.core.dao.governance.proposal.confiscatebond;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.dao.exceptions.ValidationException;
import bisq.core.dao.governance.proposal.BaseProposalService;
import bisq.core.dao.governance.proposal.BaseProposalFactory;
import bisq.core.dao.governance.proposal.ProposalWithTransaction;
import bisq.core.dao.governance.proposal.TxException;
import bisq.core.dao.state.DaoStateService;
@ -36,7 +36,7 @@ import lombok.extern.slf4j.Slf4j;
* Creates ConfiscateBondProposal and transaction.
*/
@Slf4j
public class ConfiscateBondProposalService extends BaseProposalService<ConfiscateBondProposal> {
public class ConfiscateBondProposalFactory extends BaseProposalFactory<ConfiscateBondProposal> {
private byte[] hash;
@ -45,7 +45,7 @@ public class ConfiscateBondProposalService extends BaseProposalService<Confiscat
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public ConfiscateBondProposalService(BsqWalletService bsqWalletService,
public ConfiscateBondProposalFactory(BsqWalletService bsqWalletService,
BtcWalletService btcWalletService,
DaoStateService daoStateService,
ConfiscateBondValidator proposalValidator) {

View file

@ -20,7 +20,7 @@ package bisq.core.dao.governance.proposal.generic;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.dao.exceptions.ValidationException;
import bisq.core.dao.governance.proposal.BaseProposalService;
import bisq.core.dao.governance.proposal.BaseProposalFactory;
import bisq.core.dao.governance.proposal.ProposalWithTransaction;
import bisq.core.dao.governance.proposal.TxException;
import bisq.core.dao.state.DaoStateService;
@ -36,7 +36,7 @@ import lombok.extern.slf4j.Slf4j;
* Creates GenericProposal and transaction.
*/
@Slf4j
public class GenericProposalService extends BaseProposalService<GenericProposal> {
public class GenericProposalFactory extends BaseProposalFactory<GenericProposal> {
///////////////////////////////////////////////////////////////////////////////////////////
@ -44,7 +44,7 @@ public class GenericProposalService extends BaseProposalService<GenericProposal>
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public GenericProposalService(BsqWalletService bsqWalletService,
public GenericProposalFactory(BsqWalletService bsqWalletService,
BtcWalletService btcWalletService,
DaoStateService daoStateService,
GenericProposalValidator proposalValidator) {

View file

@ -21,7 +21,7 @@ import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.dao.exceptions.ValidationException;
import bisq.core.dao.governance.param.Param;
import bisq.core.dao.governance.proposal.BaseProposalService;
import bisq.core.dao.governance.proposal.BaseProposalFactory;
import bisq.core.dao.governance.proposal.ProposalWithTransaction;
import bisq.core.dao.governance.proposal.TxException;
import bisq.core.dao.state.DaoStateService;
@ -37,7 +37,7 @@ import lombok.extern.slf4j.Slf4j;
* Creates ChangeParamProposal and transaction.
*/
@Slf4j
public class ChangeParamProposalService extends BaseProposalService<ChangeParamProposal> {
public class ChangeParamProposalFactory extends BaseProposalFactory<ChangeParamProposal> {
private Param param;
private String paramValue;
@ -47,7 +47,7 @@ public class ChangeParamProposalService extends BaseProposalService<ChangeParamP
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public ChangeParamProposalService(BsqWalletService bsqWalletService,
public ChangeParamProposalFactory(BsqWalletService bsqWalletService,
BtcWalletService btcWalletService,
DaoStateService daoStateService,
ChangeParamValidator proposalValidator) {

View file

@ -22,7 +22,7 @@ import bisq.core.btc.exceptions.WalletException;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.dao.exceptions.ValidationException;
import bisq.core.dao.governance.proposal.BaseProposalService;
import bisq.core.dao.governance.proposal.BaseProposalFactory;
import bisq.core.dao.governance.proposal.ProposalConsensus;
import bisq.core.dao.governance.proposal.ProposalWithTransaction;
import bisq.core.dao.governance.proposal.TxException;
@ -45,13 +45,13 @@ import lombok.extern.slf4j.Slf4j;
* Creates the ReimbursementProposal and the transaction.
*/
@Slf4j
public class ReimbursementProposalService extends BaseProposalService<ReimbursementProposal> {
public class ReimbursementProposalFactory extends BaseProposalFactory<ReimbursementProposal> {
private Coin requestedBsq;
private String bsqAddress;
@Inject
public ReimbursementProposalService(BsqWalletService bsqWalletService,
public ReimbursementProposalFactory(BsqWalletService bsqWalletService,
BtcWalletService btcWalletService,
DaoStateService daoStateService,
ReimbursementValidator proposalValidator) {

View file

@ -20,7 +20,7 @@ package bisq.core.dao.governance.proposal.removeAsset;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.dao.exceptions.ValidationException;
import bisq.core.dao.governance.proposal.BaseProposalService;
import bisq.core.dao.governance.proposal.BaseProposalFactory;
import bisq.core.dao.governance.proposal.ProposalWithTransaction;
import bisq.core.dao.governance.proposal.TxException;
import bisq.core.dao.state.DaoStateService;
@ -38,7 +38,7 @@ import lombok.extern.slf4j.Slf4j;
* Creates RemoveAssetProposal and transaction.
*/
@Slf4j
public class RemoveAssetProposalService extends BaseProposalService<RemoveAssetProposal> {
public class RemoveAssetProposalFactory extends BaseProposalFactory<RemoveAssetProposal> {
private Asset asset;
@ -47,7 +47,7 @@ public class RemoveAssetProposalService extends BaseProposalService<RemoveAssetP
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public RemoveAssetProposalService(BsqWalletService bsqWalletService,
public RemoveAssetProposalFactory(BsqWalletService bsqWalletService,
BtcWalletService btcWalletService,
DaoStateService daoStateService,
RemoveAssetValidator proposalValidator) {

View file

@ -20,7 +20,7 @@ package bisq.core.dao.governance.proposal.role;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.dao.exceptions.ValidationException;
import bisq.core.dao.governance.proposal.BaseProposalService;
import bisq.core.dao.governance.proposal.BaseProposalFactory;
import bisq.core.dao.governance.proposal.ProposalWithTransaction;
import bisq.core.dao.governance.proposal.TxException;
import bisq.core.dao.state.DaoStateService;
@ -37,7 +37,7 @@ import lombok.extern.slf4j.Slf4j;
* Creates RoleProposal and transaction.
*/
@Slf4j
public class RoleProposalService extends BaseProposalService<RoleProposal> {
public class RoleProposalFactory extends BaseProposalFactory<RoleProposal> {
private Role role;
@ -46,7 +46,7 @@ public class RoleProposalService extends BaseProposalService<RoleProposal> {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public RoleProposalService(BsqWalletService bsqWalletService,
public RoleProposalFactory(BsqWalletService bsqWalletService,
BtcWalletService btcWalletService,
DaoStateService daoStateService,
RoleValidator proposalValidator) {

View file

@ -687,7 +687,11 @@ public class DaoStateService implements DaoSetupService {
return opTxOutput.isPresent() && isUnlockingOutput(opTxOutput.get());
}
// TODO SQ i changed the code here. i think it was wrong before
public boolean isUnlocking(String unlockTxId) {
Optional<Tx> optionalTx = getTx(unlockTxId);
return optionalTx.isPresent() && isUnlockingOutput(optionalTx.get().getTxOutputs().get(0));
}
public boolean isUnlockingOutput(TxOutput unlockTxOutput) {
return unlockTxOutput.getTxOutputType() == TxOutputType.UNLOCK_OUTPUT &&
!isLockTimeOverForUnlockTxOutput(unlockTxOutput);
@ -756,11 +760,6 @@ public class DaoStateService implements DaoSetupService {
// txOutput.setTxOutputType(TxOutputType.BTC_OUTPUT);
}
public boolean isUnlocking(String unlockTxId) {
Optional<Tx> optionalTx = getTx(unlockTxId);
return optionalTx.isPresent() && isUnlockingOutput(optionalTx.get().getTxOutputs().get(0));
}
///////////////////////////////////////////////////////////////////////////////////////////
// Param

View file

@ -1382,7 +1382,7 @@ dao.bond.bondedReputation=Bonded Reputation
dao.bond.table.header=Bonded roles
dao.bond.table.column.header.name=Name
dao.bond.table.column.header.linkToAccount=Account
dao.bond.table.column.header.link=Link
dao.bond.table.column.header.bondedRoleType=Role
dao.bond.table.column.header.startDate=Started
dao.bond.table.column.header.lockupTxId=Lockup Tx ID
@ -1397,6 +1397,21 @@ dao.bond.table.lockedUp=Bond locked up
dao.bond.table.unlocking=Bond unlocking
dao.bond.table.unlocked=Bond unlocked
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleState.READY_FOR_LOCKUP=Not bonded yet
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleState.LOCKUP_TX_PENDING=Lockup pending
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleState.LOCKUP_TX_CONFIRMED=Bond locked up
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleState.UNLOCK_TX_PENDING=Unlock pending
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleState.UNLOCK_TX_CONFIRMED=Unlock tx confirmed
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleState.UNLOCKING=Bond unlocking
# suppress inspection "UnusedProperty"
dao.bond.bondedRoleState.UNLOCKED=Bond unlocked
# suppress inspection "UnusedProperty"
dao.phase.UNDEFINED=Undefined
# suppress inspection "UnusedProperty"

View file

@ -1163,7 +1163,7 @@ dao.bond.bondedRoleType.details.blocks={0} Blöcke
dao.bond.table.header=Gekoppelte Rollen
dao.bond.table.column.header.name=Name
dao.bond.table.column.header.linkToAccount=Konto
dao.bond.table.column.header.link=Konto
dao.bond.table.column.header.bondedRoleType=Rolle
dao.bond.table.column.header.startDate=Gestartet
dao.bond.table.column.header.lockupTxId=Sperrung Tx ID

View file

@ -1163,7 +1163,7 @@ dao.bond.bondedRoleType.details.blocks={0} blocks
dao.bond.table.header=Bonded roles
dao.bond.table.column.header.name=Όνομα
dao.bond.table.column.header.linkToAccount=Λογαριασμός
dao.bond.table.column.header.link=Λογαριασμός
dao.bond.table.column.header.bondedRoleType=Ρόλος
dao.bond.table.column.header.startDate=Started
dao.bond.table.column.header.lockupTxId=Lockup Tx ID

View file

@ -1163,7 +1163,7 @@ dao.bond.bondedRoleType.details.blocks={0} blocks
dao.bond.table.header=Bonded roles
dao.bond.table.column.header.name=Nombre
dao.bond.table.column.header.linkToAccount=Cuenta
dao.bond.table.column.header.link=Cuenta
dao.bond.table.column.header.bondedRoleType=Rol
dao.bond.table.column.header.startDate=Started
dao.bond.table.column.header.lockupTxId=Lockup Tx ID

View file

@ -1163,7 +1163,7 @@ dao.bond.bondedRoleType.details.blocks={0} blocks
dao.bond.table.header=Bonded roles
dao.bond.table.column.header.name=نام
dao.bond.table.column.header.linkToAccount=حساب
dao.bond.table.column.header.link=حساب
dao.bond.table.column.header.bondedRoleType=نقش
dao.bond.table.column.header.startDate=Started
dao.bond.table.column.header.lockupTxId=Lockup Tx ID

View file

@ -1163,7 +1163,7 @@ dao.bond.bondedRoleType.details.blocks={0} blocks
dao.bond.table.header=Bonded roles
dao.bond.table.column.header.name=Név
dao.bond.table.column.header.linkToAccount=Fiók
dao.bond.table.column.header.link=Fiók
dao.bond.table.column.header.bondedRoleType=Szerep
dao.bond.table.column.header.startDate=Started
dao.bond.table.column.header.lockupTxId=Lockup Tx ID

View file

@ -1163,7 +1163,7 @@ dao.bond.bondedRoleType.details.blocks={0} blocks
dao.bond.table.header=Bonded roles
dao.bond.table.column.header.name=Nome
dao.bond.table.column.header.linkToAccount=Conta
dao.bond.table.column.header.link=Conta
dao.bond.table.column.header.bondedRoleType=Função
dao.bond.table.column.header.startDate=Started
dao.bond.table.column.header.lockupTxId=Lockup Tx ID

View file

@ -1163,7 +1163,7 @@ dao.bond.bondedRoleType.details.blocks={0} blocks
dao.bond.table.header=Bonded roles
dao.bond.table.column.header.name=Nume
dao.bond.table.column.header.linkToAccount=Cont
dao.bond.table.column.header.link=Cont
dao.bond.table.column.header.bondedRoleType=Rol
dao.bond.table.column.header.startDate=Started
dao.bond.table.column.header.lockupTxId=Lockup Tx ID

View file

@ -1163,7 +1163,7 @@ dao.bond.bondedRoleType.details.blocks={0} блоков
dao.bond.table.header=Обеспеченные роли
dao.bond.table.column.header.name=Имя
dao.bond.table.column.header.linkToAccount=Счёт
dao.bond.table.column.header.link=Счёт
dao.bond.table.column.header.bondedRoleType=Роль
dao.bond.table.column.header.startDate=Начато
dao.bond.table.column.header.lockupTxId=Транз. идент. блокировки

View file

@ -1163,7 +1163,7 @@ dao.bond.bondedRoleType.details.blocks={0} blocks
dao.bond.table.header=Bonded roles
dao.bond.table.column.header.name=Ime
dao.bond.table.column.header.linkToAccount=Nalog
dao.bond.table.column.header.link=Nalog
dao.bond.table.column.header.bondedRoleType=Uloga
dao.bond.table.column.header.startDate=Started
dao.bond.table.column.header.lockupTxId=Lockup Tx ID

View file

@ -1163,7 +1163,7 @@ dao.bond.bondedRoleType.details.blocks={0} blocks
dao.bond.table.header=Bonded roles
dao.bond.table.column.header.name=ชื่อ
dao.bond.table.column.header.linkToAccount=บัญชี
dao.bond.table.column.header.link=บัญชี
dao.bond.table.column.header.bondedRoleType=บทบาท
dao.bond.table.column.header.startDate=Started
dao.bond.table.column.header.lockupTxId=Lockup Tx ID

View file

@ -1163,7 +1163,7 @@ dao.bond.bondedRoleType.details.blocks={0} blocks
dao.bond.table.header=Bonded roles
dao.bond.table.column.header.name=Tên
dao.bond.table.column.header.linkToAccount=Tài khoản
dao.bond.table.column.header.link=Tài khoản
dao.bond.table.column.header.bondedRoleType=Vai trò
dao.bond.table.column.header.startDate=Started
dao.bond.table.column.header.lockupTxId=Lockup Tx ID

View file

@ -1163,7 +1163,7 @@ dao.bond.bondedRoleType.details.blocks={0} blocks
dao.bond.table.header=Bonded roles
dao.bond.table.column.header.name=名称
dao.bond.table.column.header.linkToAccount=账户
dao.bond.table.column.header.link=账户
dao.bond.table.column.header.bondedRoleType=角色
dao.bond.table.column.header.startDate=Started
dao.bond.table.column.header.lockupTxId=Lockup Tx ID

View file

@ -38,7 +38,6 @@ import bisq.core.util.BsqFormatter;
import bisq.network.p2p.P2PService;
import bisq.common.app.DevEnv;
import bisq.common.handlers.ResultHandler;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
@ -46,9 +45,12 @@ import org.bitcoinj.core.InsufficientMoneyException;
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;
@Slf4j
public class BondingViewUtils {
@ -69,7 +71,7 @@ public class BondingViewUtils {
}
private void lockupBond(BondWithHash bondWithHash, Coin lockupAmount, int lockupTime, LockupType lockupType,
ResultHandler resultHandler) {
Consumer<String> resultHandler) {
if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) {
if (!DevEnv.isDevMode()) {
new Popup<>().headLine(Res.get("dao.bonding.lock.sendFunds.headline"))
@ -89,41 +91,38 @@ public class BondingViewUtils {
}
}
private void publishLockupTx(BondWithHash bondWithHash, Coin lockupAmount, int lockupTime, LockupType lockupType, ResultHandler resultHandler) {
private void publishLockupTx(BondWithHash bondWithHash, Coin lockupAmount, int lockupTime, LockupType lockupType, Consumer<String> resultHandler) {
daoFacade.publishLockupTx(lockupAmount,
lockupTime,
lockupType,
bondWithHash,
() -> {
txId -> {
if (!DevEnv.isDevMode())
new Popup<>().feedback(Res.get("dao.tx.published.success")).show();
if (resultHandler != null)
resultHandler.accept(txId);
},
this::handleError
);
if (resultHandler != null)
resultHandler.handleResult();
}
public void lockupBondForBondedRole(Role role, ResultHandler resultHandler) {
public void lockupBondForBondedRole(Role role, Consumer<String> resultHandler) {
BondedRoleType bondedRoleType = role.getBondedRoleType();
Coin lockupAmount = Coin.valueOf(bondedRoleType.getRequiredBond());
int lockupTime = bondedRoleType.getUnlockTimeInBlocks();
lockupBond(role, lockupAmount, lockupTime, LockupType.BONDED_ROLE, resultHandler);
}
public void lockupBondForReputation(Coin lockupAmount, int lockupTime, ResultHandler resultHandler) {
public void lockupBondForReputation(Coin lockupAmount, int lockupTime, Consumer<String> resultHandler) {
BondedReputation bondedReputation = BondedReputation.createBondedReputation();
lockupBond(bondedReputation, lockupAmount, lockupTime, LockupType.REPUTATION, resultHandler);
}
public void unLock(String lockupTxId, ResultHandler resultHandler) {
public void unLock(String lockupTxId, Consumer<String> resultHandler) {
if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) {
Optional<TxOutput> lockupTxOutput = daoFacade.getLockupTxOutput(lockupTxId);
if (!lockupTxOutput.isPresent()) {
log.warn("Lockup output not found, txId = ", lockupTxId);
return;
}
checkArgument(lockupTxOutput.isPresent(), "Lockup output must be present. TxId=" + lockupTxId);
Coin unlockAmount = Coin.valueOf(lockupTxOutput.get().getValue());
Optional<Integer> opLockTime = daoFacade.getLockTime(lockupTxId);
int lockTime = opLockTime.orElse(-1);
@ -153,13 +152,14 @@ public class BondingViewUtils {
log.info("unlock tx: {}", lockupTxId);
}
private void publishUnlockTx(String lockupTxId, ResultHandler resultHandler) {
private void publishUnlockTx(String lockupTxId, Consumer<String> resultHandler) {
daoFacade.publishUnlockTx(lockupTxId,
() -> {
txId -> {
if (!DevEnv.isDevMode())
new Popup<>().confirmation(Res.get("dao.tx.published.success")).show();
resultHandler.handleResult();
if (resultHandler != null)
resultHandler.accept(txId);
},
errorMessage -> new Popup<>().warning(errorMessage.toString()).show()
);

View file

@ -150,7 +150,7 @@ public class LockupView extends ActivatableView<GridPane, Void> implements BsqBa
bondedRolesComboBox.setVisible(true);
lockupRows++;
bondedRolesComboBox.setItems(FXCollections.observableArrayList(daoFacade.getBondedRoleStates()));
bondedRolesComboBox.setItems(FXCollections.observableArrayList(daoFacade.getBondedRoles()));
} else {
bondedRolesComboBox.setVisible(false);
bondedRolesComboBox.getItems().clear();
@ -199,13 +199,13 @@ public class LockupView extends ActivatableView<GridPane, Void> implements BsqBa
case BONDED_ROLE:
if (bondedRolesComboBox.getValue() != null) {
bondingViewUtils.lockupBondForBondedRole(bondedRolesComboBox.getValue().getRole(),
() -> bondedRolesComboBox.getSelectionModel().clearSelection());
txId -> bondedRolesComboBox.getSelectionModel().clearSelection());
}
break;
case REPUTATION:
bondingViewUtils.lockupBondForReputation(bsqFormatter.parseToCoin(amountInputTextField.getText()),
Integer.parseInt(timeInputTextField.getText()),
() -> {
txId -> {
amountInputTextField.setText("");
timeInputTextField.setText("");
});

View file

@ -18,9 +18,11 @@
package bisq.desktop.main.dao.bonding.roles;
import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.main.dao.bonding.BondingViewUtils;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.governance.bond.BondedRole;
import bisq.core.dao.governance.bond.BondedRoleState;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.governance.Role;
@ -31,37 +33,65 @@ import javafx.scene.control.Label;
import java.util.Date;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@EqualsAndHashCode
@Data
class BondedRolesListItem implements DaoStateListener {
@Getter
private final BondedRole bondedRole;
private final DaoFacade daoFacade;
private final BondingViewUtils bondingViewUtils;
private final BsqFormatter bsqFormatter;
@Getter
private final AutoTooltipButton button;
@Getter
private final Label label;
@Getter
private final Role role;
private final boolean isMyRole;
BondedRolesListItem(BondedRole bondedRole,
DaoFacade daoFacade,
BondingViewUtils bondingViewUtils,
BsqFormatter bsqFormatter) {
this.bondedRole = bondedRole;
this.daoFacade = daoFacade;
this.bondingViewUtils = bondingViewUtils;
this.bsqFormatter = bsqFormatter;
role = bondedRole.getRole();
isMyRole = daoFacade.isMyRole(bondedRole.getRole());
daoFacade.addBsqStateListener(this);
button = new AutoTooltipButton();
button.setMinWidth(70);
label = new Label();
button.setOnAction(e -> {
if (bondedRole.getBondedRoleState() == BondedRoleState.READY_FOR_LOCKUP) {
bondingViewUtils.lockupBondForBondedRole(role,
txId -> {
bondedRole.setLockupTxId(txId);
bondedRole.setBondedRoleState(BondedRoleState.LOCKUP_TX_PENDING);
update();
button.setDisable(true);
});
} else if (bondedRole.getBondedRoleState() == BondedRoleState.LOCKUP_TX_CONFIRMED) {
bondingViewUtils.unLock(bondedRole.getLockupTxId(),
txId -> {
bondedRole.setUnlockTxId(txId);
bondedRole.setBondedRoleState(BondedRoleState.UNLOCK_TX_PENDING);
update();
button.setDisable(true);
});
}
});
update();
}
@ -79,48 +109,27 @@ class BondedRolesListItem implements DaoStateListener {
public void cleanup() {
daoFacade.removeBsqStateListener(this);
setOnAction(null);
button.setOnAction(null);
}
public void setOnAction(Runnable handler) {
button.setOnAction(e -> handler.run());
}
public boolean isBonded() {
return bondedRole.isLockedUp();
}
private void update() {
// We have following state:
// 1. Not bonded: !isLockedUp, !isUnlocked, !isUnlocking: notBonded
// 2. Locked up: isLockedUp, !isUnlocked, !isUnlocking: lockedUp
// 3. Unlocking: isLockedUp, isUnlocked, isUnlocking: unlocking
// 4. Unlocked: isLockedUp, isUnlocked, !isUnlocking: unlocked
label.setText(Res.get("dao.bond.bondedRoleState." + bondedRole.getBondedRoleState().name()));
boolean isLockedUp = bondedRole.isLockedUp();
boolean isUnlocked = bondedRole.isUnlocked();
boolean isUnlocking = bondedRole.isUnlocking();
log.error("bondedRole.getLockupTxId()={}, bondedRole.getBondedRoleState()={}", bondedRole.getLockupTxId(), bondedRole.getBondedRoleState());
boolean showLockup = bondedRole.getBondedRoleState() == BondedRoleState.READY_FOR_LOCKUP;
boolean showRevoke = bondedRole.getBondedRoleState() == BondedRoleState.LOCKUP_TX_CONFIRMED;
if (showLockup)
button.updateText(Res.get("dao.bond.table.button.lockup"));
else if (showRevoke)
button.updateText(Res.get("dao.bond.table.button.revoke"));
String text;
if (!isLockedUp)
text = Res.get("dao.bond.table.notBonded");
else if (!isUnlocked)
text = Res.get("dao.bond.table.lockedUp");
else if (isUnlocking)
text = Res.get("dao.bond.table.unlocking");
else
text = Res.get("dao.bond.table.unlocked");
label.setText(text);
button.updateText(isLockedUp ? Res.get("dao.bond.table.button.revoke") : Res.get("dao.bond.table.button.lockup"));
button.setVisible(!isLockedUp || !isUnlocked);
button.setManaged(button.isVisible());
//TODO listen to unconfirmed txs and update button and label state
boolean showButton = isMyRole && (showLockup || showRevoke);
button.setVisible(showButton);
button.setManaged(showButton);
}
// DaoStateListener
@Override
public void onNewBlockHeight(int blockHeight) {

View file

@ -153,8 +153,8 @@ public class BondedRolesView extends ActivatableView<GridPane, Void> implements
private void updateList() {
observableList.forEach(BondedRolesListItem::cleanup);
observableList.setAll(daoFacade.getBondedRoleStates().stream()
.map(bondedRoleState -> new BondedRolesListItem(bondedRoleState, daoFacade, bsqFormatter))
observableList.setAll(daoFacade.getBondedRoles().stream()
.map(bondedRole -> new BondedRolesListItem(bondedRole, daoFacade, bondingViewUtils, bsqFormatter))
.collect(Collectors.toList()));
}
@ -195,7 +195,7 @@ public class BondedRolesView extends ActivatableView<GridPane, Void> implements
});
tableView.getColumns().add(column);
column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.header.linkToAccount"));
column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.header.link"));
column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setMinWidth(60);
column.setCellFactory(
@ -315,7 +315,7 @@ public class BondedRolesView extends ActivatableView<GridPane, Void> implements
column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.header.lockupTxId"));
column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setMinWidth(60);
column.setMinWidth(80);
column.setCellFactory(
new Callback<TableColumn<BondedRolesListItem, BondedRolesListItem>, TableCell<BondedRolesListItem,
BondedRolesListItem>>() {
@ -357,7 +357,7 @@ public class BondedRolesView extends ActivatableView<GridPane, Void> implements
column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.header.unlockTxId"));
column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setMinWidth(60);
column.setMinWidth(80);
column.setCellFactory(
new Callback<TableColumn<BondedRolesListItem, BondedRolesListItem>, TableCell<BondedRolesListItem,
BondedRolesListItem>>() {
@ -447,16 +447,6 @@ public class BondedRolesView extends ActivatableView<GridPane, Void> implements
if (item != null && !empty) {
if (button == null) {
button = item.getButton();
item.setOnAction(() -> {
if (item.isBonded())
bondingViewUtils.unLock(item.getBondedRole().getLockupTxId(),
() -> {
// TODO
button.setDisable(true);
});
else
bondingViewUtils.lockupBondForBondedRole(item.getRole(), null);
});
setGraphic(button);
}
} else {

View file

@ -397,7 +397,7 @@ public class UnlockView extends ActivatableView<GridPane, Void> implements BsqBa
if (item != null && !empty) {
if (button == null) {
button = item.getButton();
button.setOnAction(e -> bondingViewUtils.unLock(item.getTxId(), () -> {
button.setOnAction(e -> bondingViewUtils.unLock(item.getTxId(), txId -> {
//TODO
button.setDisable(true);
}));