Merge branch 'bonded-reputation' of https://github.com/sqrrm/bisq-desktop into sqrrm-bonded-reputation

# Conflicts:
#	common/src/main/proto/pb.proto
#	core/src/main/java/bisq/core/app/misc/AppSetupWithP2PAndDAO.java
#	core/src/main/java/bisq/core/dao/DaoFacade.java
#	core/src/main/java/bisq/core/dao/governance/role/BondedRole.java
#	desktop/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java
This commit is contained in:
Manfred Karrer 2018-10-31 13:17:57 -05:00
commit a8b833c285
No known key found for this signature in database
GPG Key ID: 401250966A6B2C46
17 changed files with 565 additions and 31 deletions

View File

@ -943,7 +943,8 @@ message PersistableEnvelope {
MeritList merit_list = 23;
BondedRoleList bonded_role_list = 24;
RemovedAssetList removed_asset_list = 25;
DaoStateStore dao_state_store = 28;
DaoStateStore dao_state_store = 26;
BondedReputationList bonded_reputation_list = 27;
}
}
@ -1554,6 +1555,16 @@ message BondedRoleList {
repeated BondedRole bonded_role = 1;
}
message BondedReputation {
string salt = 1;
string lockup_tx_id = 2;
string unlock_tx_id = 3;
}
message BondedReputationList {
repeated BondedReputation bonded_reputation = 1;
}
message TempProposalPayload {
Proposal proposal = 1;
bytes owner_pub_key_encoded = 2;

View File

@ -18,6 +18,7 @@
package bisq.core.app.misc;
import bisq.core.dao.DaoSetup;
import bisq.core.dao.bonding.bond.BondedReputationService;
import bisq.core.dao.governance.asset.AssetService;
import bisq.core.dao.governance.ballot.BallotListService;
import bisq.core.dao.governance.blindvote.MyBlindVoteListService;
@ -52,6 +53,7 @@ public class AppSetupWithP2PAndDAO extends AppSetupWithP2P {
BallotListService ballotListService,
MyBlindVoteListService myBlindVoteListService,
BondedRolesService bondedRolesService,
BondedReputationService bondedReputationService,
AssetService assetService) {
super(encryptionService,
keyRing,
@ -66,6 +68,7 @@ public class AppSetupWithP2PAndDAO extends AppSetupWithP2P {
persistedDataHosts.add(ballotListService);
persistedDataHosts.add(myBlindVoteListService);
persistedDataHosts.add(bondedRolesService);
persistedDataHosts.add(bondedReputationService);
persistedDataHosts.add(assetService);
}

View File

@ -19,6 +19,9 @@ package bisq.core.dao;
import bisq.core.btc.exceptions.TransactionVerificationException;
import bisq.core.btc.exceptions.WalletException;
import bisq.core.dao.bonding.bond.BondWithHash;
import bisq.core.dao.bonding.bond.BondedReputation;
import bisq.core.dao.bonding.bond.BondedReputationService;
import bisq.core.dao.bonding.lockup.LockupService;
import bisq.core.dao.bonding.lockup.LockupType;
import bisq.core.dao.bonding.unlock.UnlockService;
@ -111,6 +114,7 @@ public class DaoFacade implements DaoSetupService {
private final GenericProposalService genericProposalService;
private final RemoveAssetProposalService removeAssetProposalService;
private final BondedRolesService bondedRolesService;
private final BondedReputationService bondedReputationService;
private final LockupService lockupService;
private final UnlockService unlockService;
private final DaoStateStorageService daoStateStorageService;
@ -134,6 +138,7 @@ public class DaoFacade implements DaoSetupService {
GenericProposalService genericProposalService,
RemoveAssetProposalService removeAssetProposalService,
BondedRolesService bondedRolesService,
BondedReputationService bondedReputationService,
LockupService lockupService,
UnlockService unlockService,
DaoStateStorageService daoStateStorageService) {
@ -153,6 +158,7 @@ public class DaoFacade implements DaoSetupService {
this.genericProposalService = genericProposalService;
this.removeAssetProposalService = removeAssetProposalService;
this.bondedRolesService = bondedRolesService;
this.bondedReputationService = bondedReputationService;
this.lockupService = lockupService;
this.unlockService = unlockService;
this.daoStateStorageService = daoStateStorageService;
@ -277,6 +283,10 @@ public class DaoFacade implements DaoSetupService {
return bondedRolesService.getBondedRoleList();
}
public List<BondedReputation> getBondedReputationList() {
return bondedReputationService.getBondedReputationList();
}
// Show fee
public Coin getProposalFee(int chainHeight) {
return ProposalConsensus.getFee(daoStateService, chainHeight);
@ -474,9 +484,9 @@ public class DaoFacade implements DaoSetupService {
// Use case: Bonding
///////////////////////////////////////////////////////////////////////////////////////////
public void publishLockupTx(Coin lockupAmount, int lockTime, LockupType lockupType, BondedRole bondedRole,
public void publishLockupTx(Coin lockupAmount, int lockTime, LockupType lockupType, BondWithHash bondWithHash,
ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
lockupService.publishLockupTx(lockupAmount, lockTime, lockupType, bondedRole, resultHandler, exceptionHandler);
lockupService.publishLockupTx(lockupAmount, lockTime, lockupType, bondWithHash, resultHandler, exceptionHandler);
}
public void publishUnlockTx(String lockupTxId, ResultHandler resultHandler,
@ -504,6 +514,10 @@ public class DaoFacade implements DaoSetupService {
return bondedRolesService.getValidBondedRoleList();
}
public List<BondedReputation> getValidBondedReputationList() {
return bondedReputationService.getValidBondedReputationList();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Use case: Present transaction related state
@ -605,8 +619,12 @@ public class DaoFacade implements DaoSetupService {
return bondedRolesService.getBondedRoleFromHash(hash);
}
public boolean isUnlocking(BondedRole bondedRole) {
return daoStateService.isUnlocking(bondedRole);
public Optional<BondedReputation> getBondedReputationFromHash(byte[] hash) {
return bondedReputationService.getBondedReputationFromHash(hash);
}
public boolean isUnlocking(BondWithHash bondWithHash) {
return daoStateService.isUnlocking(bondWithHash);
}
public Coin getMinCompensationRequestAmount() {

View File

@ -17,6 +17,7 @@
package bisq.core.dao;
import bisq.core.dao.bonding.bond.BondedReputationService;
import bisq.core.dao.bonding.lockup.LockupService;
import bisq.core.dao.bonding.unlock.UnlockService;
import bisq.core.dao.governance.asset.AssetService;
@ -186,6 +187,7 @@ public class DaoModule extends AppModule {
bind(LockupService.class).in(Singleton.class);
bind(UnlockService.class).in(Singleton.class);
bind(BondedRolesService.class).in(Singleton.class);
bind(BondedReputationService.class).in(Singleton.class);
// Asset
bind(AssetService.class).in(Singleton.class);

View File

@ -17,8 +17,8 @@
package bisq.core.dao.bonding;
import bisq.core.dao.bonding.bond.BondWithHash;
import bisq.core.dao.bonding.lockup.LockupType;
import bisq.core.dao.governance.role.BondedRole;
import bisq.core.dao.state.blockchain.OpReturnType;
import bisq.common.app.Version;
@ -89,11 +89,7 @@ public class BondingConsensus {
return Arrays.copyOfRange(opReturnData, 5, 25);
}
public static byte[] getHash(LockupType lockupType, BondedRole bondedRole) {
if (lockupType == LockupType.BONDED_ROLE) {
return bondedRole.getHash();
} else {
throw new RuntimeException("Trade bonds not implemented yet");
}
public static byte[] getHash(BondWithHash bondWithHash) {
return bondWithHash.getHash();
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.bonding.bond;
public interface BondWithHash {
String getUnlockTxId();
byte[] getHash();
}

View File

@ -0,0 +1,149 @@
/*
* 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.bonding.bond;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.state.BsqStateService;
import bisq.core.locale.Res;
import bisq.common.crypto.Hash;
import bisq.common.proto.network.NetworkPayload;
import bisq.common.proto.persistable.PersistablePayload;
import io.bisq.generated.protobuffer.PB;
import java.math.BigInteger;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import javax.annotation.Nullable;
@Getter
public final class BondedReputation implements PersistablePayload, NetworkPayload, BondWithHash {
private final String salt;
@Setter
private String lockupTxId;
@Nullable
@Setter
private String unlockTxId;
public BondedReputation(@Nullable String salt) {
this(salt,
null,
null
);
}
public static BondedReputation createBondedReputation() {
return new BondedReputation(UUID.randomUUID().toString());
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
public BondedReputation(String salt,
@Nullable String lockupTxId,
@Nullable String unlockTxId) {
this.salt = salt;
this.lockupTxId = lockupTxId;
this.unlockTxId = unlockTxId;
}
@Override
public PB.BondedReputation toProtoMessage() {
PB.BondedReputation.Builder builder = PB.BondedReputation.newBuilder()
.setSalt(salt);
Optional.ofNullable(lockupTxId).ifPresent(builder::setLockupTxId);
Optional.ofNullable(unlockTxId).ifPresent(builder::setUnlockTxId);
return builder.build();
}
public static BondedReputation fromProto(PB.BondedReputation proto) {
return new BondedReputation(proto.getSalt(),
proto.getLockupTxId().isEmpty() ? null : proto.getLockupTxId(),
proto.getUnlockTxId().isEmpty() ? null : proto.getUnlockTxId());
}
///////////////////////////////////////////////////////////////////////////////////////////
// Utils
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public String getUnlockTxId() {
return unlockTxId;
}
@Override
public byte[] getHash() {
// We use the salt as input for the hash
byte[] bytes = BigInteger.valueOf(hashCode()).toByteArray();
byte[] hash = Hash.getSha256Ripemd160hash(bytes);
return hash;
}
public String getDisplayString() {
return Res.get("dao.bond.bondedReputation");
}
public boolean isLockedUp() {
return lockupTxId != null;
}
public boolean isUnlocked() {
return unlockTxId != null;
}
public boolean isUnlocking(DaoFacade daoFacade) {
return daoFacade.isUnlocking(this);
}
public boolean isUnlocking(BsqStateService bsqStateService) {
return bsqStateService.isUnlocking(this);
}
// We use only the immutable data
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BondedReputation that = (BondedReputation) o;
return Objects.equals(salt, that.salt);
}
@Override
public int hashCode() {
return Objects.hash(salt);
}
@Override
public String toString() {
return "BondedReputation{" +
"\n salt='" + salt + '\'' +
",\n lockupTxId='" + lockupTxId + '\'' +
",\n unlockTxId='" + unlockTxId + '\'' +
"\n}";
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.bonding.bond;
import bisq.common.proto.persistable.PersistableList;
import io.bisq.generated.protobuffer.PB;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import lombok.EqualsAndHashCode;
/**
* PersistableEnvelope wrapper for list of BondedReputations.
*/
@EqualsAndHashCode(callSuper = true)
public class BondedReputationList extends PersistableList<BondedReputation> {
public BondedReputationList(List<BondedReputation> list) {
super(list);
}
public BondedReputationList() {
super();
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public PB.PersistableEnvelope toProtoMessage() {
return PB.PersistableEnvelope.newBuilder().setBondedReputationList(getBuilder()).build();
}
public PB.BondedReputationList.Builder getBuilder() {
return PB.BondedReputationList.newBuilder()
.addAllBondedReputation(getList().stream()
.map(BondedReputation::toProtoMessage)
.collect(Collectors.toList()));
}
public static BondedReputationList fromProto(PB.BondedReputationList proto) {
return new BondedReputationList(new ArrayList<>(proto.getBondedReputationList().stream()
.map(BondedReputation::fromProto)
.collect(Collectors.toList())));
}
@Override
public String toString() {
return "List of salts in BondedReputationList: " + getList().stream()
.map(BondedReputation::getSalt)
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,201 @@
/*
* 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.bonding.bond;
import bisq.core.app.BisqEnvironment;
import bisq.core.dao.bonding.BondingConsensus;
import bisq.core.dao.state.BsqStateListener;
import bisq.core.dao.state.BsqStateService;
import bisq.core.dao.state.blockchain.Block;
import bisq.core.dao.state.blockchain.SpentInfo;
import bisq.core.dao.state.blockchain.TxType;
import bisq.common.proto.persistable.PersistedDataHost;
import bisq.common.storage.Storage;
import javax.inject.Inject;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class BondedReputationService implements PersistedDataHost, BsqStateListener {
public interface BondedReputationListChangeListener {
void onListChanged(List<BondedReputation> list);
}
private final BsqStateService bsqStateService;
private final Storage<BondedReputationList> storage;
private final BondedReputationList BondedReputationList = new BondedReputationList();
@Getter
private final List<BondedReputationListChangeListener> listeners = new CopyOnWriteArrayList<>();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public BondedReputationService(Storage<BondedReputationList> storage, BsqStateService bsqStateService) {
this.storage = storage;
this.bsqStateService = bsqStateService;
bsqStateService.addBsqStateListener(this);
}
///////////////////////////////////////////////////////////////////////////////////////////
// PersistedDataHost
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void readPersisted() {
if (BisqEnvironment.isDAOActivatedAndBaseCurrencySupportingBsq()) {
BondedReputationList persisted = storage.initAndGetPersisted(BondedReputationList, 100);
if (persisted != null) {
BondedReputationList.clear();
BondedReputationList.addAll(persisted.getList());
listeners.forEach(l -> l.onListChanged(BondedReputationList.getList()));
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// BsqStateListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onNewBlockHeight(int blockHeight) {
}
@Override
public void onParseTxsComplete(Block block) {
BondedReputationList.getList().forEach(BondedReputation -> {
bsqStateService.getLockupTxOutputs().forEach(lockupTxOutput -> {
String lockupTxId = lockupTxOutput.getTxId();
// log.error("lockupTxId " + lockupTxId);
bsqStateService.getTx(lockupTxId)
.ifPresent(lockupTx -> {
byte[] opReturnData = lockupTx.getLastTxOutput().getOpReturnData();
byte[] hash = BondingConsensus.getHashFromOpReturnData(opReturnData);
Optional<BondedReputation> candidate = getBondedReputationFromHash(hash);
if (candidate.isPresent() && BondedReputation.equals(candidate.get())) {
if (BondedReputation.getLockupTxId() == null) {
BondedReputation.setLockupTxId(lockupTxId);
persist();
}
if (!bsqStateService.isUnspent(lockupTxOutput.getKey())) {
bsqStateService.getSpentInfo(lockupTxOutput)
.map(SpentInfo::getTxId)
.map(bsqStateService::getTx)
.map(Optional::get)
// TODO(sq): What if the tx is burnt and not unlocked, need to check on that
.filter(unlockTx -> unlockTx.getTxType() == TxType.UNLOCK)
.ifPresent(unlockTx -> {
if (BondedReputation.getUnlockTxId() == null) {
BondedReputation.setUnlockTxId(unlockTx.getId());
persist();
}
// TODO check lock time
});
}
}
});
});
});
}
@Override
public void onParseBlockChainComplete() {
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void start() {
}
public void addListener(BondedReputationListChangeListener listener) {
listeners.add(listener);
}
// public void addAcceptedBondedReputation(BondedReputation BondedReputation) {
// if (BondedReputationList.getList().stream().noneMatch(role -> role.equals(BondedReputation))) {
// BondedReputationList.add(BondedReputation);
// persist();
// listeners.forEach(l -> l.onListChanged(BondedReputationList.getList()));
// }
// }
public List<BondedReputation> getBondedReputationList() {
return BondedReputationList.getList();
}
public List<BondedReputation> getValidBondedReputationList() {
return BondedReputationList.getList();
}
public Optional<BondedReputation> getBondedReputationFromHash(byte[] hash) {
return BondedReputationList.getList().stream()
.filter(BondedReputation -> {
byte[] candidateHash = BondedReputation.getHash();
/* log.error("getBondedReputationFromHash: equals?={}, hash={}, candidateHash={}\nBondedReputation={}",
Arrays.equals(candidateHash, hash),
Utilities.bytesAsHexString(hash),
Utilities.bytesAsHexString(candidateHash),
BondedReputation.toString());*/
return Arrays.equals(candidateHash, hash);
})
.findAny();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void persist() {
storage.queueUpForSave(20);
}
/*public static Optional<BondedReputation> getBondedReputationByLockupTxId(String lockupTxId) {
return BondedReputations.stream()
.filter(BondedReputation -> BondedReputation.getLockupTxId().equals(lockupTxId)).
findAny();
}*/
public static Optional<BondedReputation> getBondedReputationByHashOfBondId(byte[] hash) {
return Optional.empty();
/* BondedReputations.stream()
.filter(BondedReputation -> Arrays.equals(BondedReputation.getHash(), hash))
.findAny();*/
}
}

View File

@ -26,7 +26,7 @@ import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.TxBroadcaster;
import bisq.core.btc.wallet.WalletsManager;
import bisq.core.dao.bonding.BondingConsensus;
import bisq.core.dao.governance.role.BondedRole;
import bisq.core.dao.bonding.bond.BondWithHash;
import bisq.core.dao.governance.role.BondedRolesService;
import bisq.common.handlers.ExceptionHandler;
@ -65,13 +65,13 @@ public class LockupService {
this.btcWalletService = btcWalletService;
}
public void publishLockupTx(Coin lockupAmount, int lockTime, LockupType lockupType, BondedRole bondedRole,
public void publishLockupTx(Coin lockupAmount, int lockTime, LockupType lockupType, BondWithHash bondWithHash,
ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
checkArgument(lockTime <= BondingConsensus.getMaxLockTime() &&
lockTime >= BondingConsensus.getMinLockTime(), "lockTime not in rage");
try {
byte[] hash = BondingConsensus.getHash(lockupType, bondedRole);
byte[] hash = BondingConsensus.getHash(bondWithHash);
byte[] opReturnData = BondingConsensus.getLockupOpReturnData(lockTime, lockupType, hash);
final Transaction lockupTx = getLockupTx(lockupAmount, opReturnData);

View File

@ -19,6 +19,7 @@ package bisq.core.dao.governance.role;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.bonding.bond.BondWithHash;
import bisq.core.locale.Res;
import bisq.common.crypto.Hash;
@ -42,7 +43,7 @@ import javax.annotation.Nullable;
@Slf4j
@Getter
public final class BondedRole implements PersistablePayload, NetworkPayload {
public final class BondedRole implements PersistablePayload, NetworkPayload, BondWithHash {
private final String uid;
private final String name;
private final String link;
@ -136,6 +137,12 @@ public final class BondedRole implements PersistablePayload, NetworkPayload {
// Utils
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public String getUnlockTxId() {
return unlockTxId;
}
@Override
public byte[] getHash() {
// We use only the immutable data as input for hash
byte[] bytes = BigInteger.valueOf(hashCode()).toByteArray();

View File

@ -68,7 +68,7 @@ public class BondedRoleList extends PersistableList<BondedRole> implements Conse
@Override
public String toString() {
return "List of UID's in BondedRoleList: " + getList().stream()
return "List of UIDs in BondedRoleList: " + getList().stream()
.map(BondedRole::getUid)
.collect(Collectors.toList());
}

View File

@ -19,6 +19,7 @@ package bisq.core.dao.state;
import bisq.core.dao.DaoSetupService;
import bisq.core.dao.bonding.BondingConsensus;
import bisq.core.dao.bonding.bond.BondWithHash;
import bisq.core.dao.governance.role.BondedRole;
import bisq.core.dao.governance.voteresult.DecryptedBallotsWithMerits;
import bisq.core.dao.governance.voteresult.EvaluatedProposal;
@ -752,8 +753,8 @@ public class DaoStateService implements DaoSetupService {
// txOutput.setTxOutputType(TxOutputType.BTC_OUTPUT);
}
public boolean isUnlocking(BondedRole bondedRole) {
Optional<Tx> optionalTx = getTx(bondedRole.getUnlockTxId());
public boolean isUnlocking(BondWithHash bondWithHash) {
Optional<Tx> optionalTx = getTx(bondWithHash.getUnlockTxId());
return optionalTx.isPresent() && isUnlockingOutput(optionalTx.get().getTxOutputs().get(0));
}

View File

@ -20,6 +20,7 @@ package bisq.core.setup;
import bisq.core.arbitration.DisputeManager;
import bisq.core.btc.model.AddressEntryList;
import bisq.core.dao.DaoOptionKeys;
import bisq.core.dao.bonding.bond.BondedReputationService;
import bisq.core.dao.governance.asset.AssetService;
import bisq.core.dao.governance.ballot.BallotListService;
import bisq.core.dao.governance.blindvote.MyBlindVoteListService;
@ -68,6 +69,7 @@ public class CorePersistedDataHost {
persistedDataHosts.add(injector.getInstance(MyVoteListService.class));
persistedDataHosts.add(injector.getInstance(MyProposalListService.class));
persistedDataHosts.add(injector.getInstance(BondedRolesService.class));
persistedDataHosts.add(injector.getInstance(BondedReputationService.class));
persistedDataHosts.add(injector.getInstance(AssetService.class));
}
return persistedDataHosts;

View File

@ -1348,6 +1348,8 @@ dao.bond.bondedRoleType.details.link=Link to role description
dao.bond.bondedRoleType.details.isSingleton=Can be taken by multiple role holders
dao.bond.bondedRoleType.details.blocks={0} blocks
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

View File

@ -26,6 +26,8 @@ import bisq.desktop.util.GUIUtil;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.bonding.bond.BondWithHash;
import bisq.core.dao.bonding.bond.BondedReputation;
import bisq.core.dao.bonding.lockup.LockupType;
import bisq.core.dao.governance.role.BondedRole;
import bisq.core.dao.governance.role.BondedRoleType;
@ -65,12 +67,9 @@ public class BondingViewUtils {
this.bsqFormatter = bsqFormatter;
}
public void lockupBondForBondedRole(BondedRole bondedRole, ResultHandler resultHandler) {
private void lockupBond(BondWithHash bondWithHash, Coin lockupAmount, int lockupTime, LockupType lockupType,
ResultHandler resultHandler) {
if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) {
BondedRoleType bondedRoleType = bondedRole.getBondedRoleType();
Coin lockupAmount = Coin.valueOf(bondedRoleType.getRequiredBond());
int lockupTime = bondedRoleType.getUnlockTime();
LockupType lockupType = LockupType.BONDED_ROLE;
new Popup<>().headLine(Res.get("dao.bonding.lock.sendFunds.headline"))
.confirmation(Res.get("dao.bonding.lock.sendFunds.details",
bsqFormatter.formatCoinWithCode(lockupAmount),
@ -81,7 +80,7 @@ public class BondingViewUtils {
daoFacade.publishLockupTx(lockupAmount,
lockupTime,
lockupType,
bondedRole,
bondWithHash,
() -> {
new Popup<>().feedback(Res.get("dao.tx.published.success")).show();
},
@ -97,6 +96,18 @@ public class BondingViewUtils {
}
}
public void lockupBondForBondedRole(BondedRole bondedRole, ResultHandler resultHandler) {
BondedRoleType bondedRoleType = bondedRole.getBondedRoleType();
Coin lockupAmount = Coin.valueOf(bondedRoleType.getRequiredBond());
int lockupTime = bondedRoleType.getUnlockTime();
lockupBond(bondedRole, lockupAmount, lockupTime, LockupType.BONDED_ROLE, resultHandler);
}
public void lockupBondForReputation(Coin lockupAmount, int lockupTime, ResultHandler resultHandler) {
BondedReputation bondedReputation = BondedReputation.createBondedReputation();
lockupBond(bondedReputation, lockupAmount, lockupTime, LockupType.REPUTATION, resultHandler);
}
public void unLock(String lockupTxId) {
if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) {
Optional<TxOutput> lockupTxOutput = daoFacade.getLockupTxOutput(lockupTxId);

View File

@ -20,6 +20,7 @@ package bisq.desktop.main.dao.bonding.lockup;
import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.InputTextField;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.main.dao.bonding.BondingViewUtils;
import bisq.desktop.main.dao.wallet.BsqBalanceUtil;
import bisq.desktop.util.FormBuilder;
@ -37,12 +38,15 @@ import bisq.core.locale.Res;
import bisq.core.util.BsqFormatter;
import bisq.core.util.validation.IntegerValidator;
import bisq.common.util.Tuple2;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.beans.value.ChangeListener;
@ -71,6 +75,7 @@ public class LockupView extends ActivatableView<GridPane, Void> implements BsqBa
private InputTextField amountInputTextField;
private InputTextField timeInputTextField;
private ComboBox<LockupType> lockupTypeComboBox;
private Label bondedRolesLabel;
private ComboBox<BondedRole> bondedRolesComboBox;
private Button lockupButton;
private ChangeListener<Boolean> focusOutListener;
@ -106,7 +111,8 @@ public class LockupView extends ActivatableView<GridPane, Void> implements BsqBa
public void initialize() {
gridRow = bsqBalanceUtil.addGroup(root, gridRow);
addTitledGroupBg(root, ++gridRow, 4, Res.get("dao.bonding.lock.lockBSQ"), Layout.GROUP_DISTANCE);
TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 4, Res.get("dao.bonding.lock.lockBSQ"),
Layout.GROUP_DISTANCE);
amountInputTextField = addInputTextField(root, gridRow, Res.get("dao.bonding.lock.amount"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE);
@ -119,6 +125,7 @@ public class LockupView extends ActivatableView<GridPane, Void> implements BsqBa
timeInputTextField.setValidator(timeInputTextFieldValidator);
lockupTypeComboBox = FormBuilder.<LockupType>addComboBox(root, ++gridRow, Res.get("dao.bonding.lock.type"));
lockupTypeComboBox.setPromptText(Res.get("shared.select"));
lockupTypeComboBox.setConverter(new StringConverter<>() {
@Override
public String toString(LockupType lockupType) {
@ -135,11 +142,23 @@ public class LockupView extends ActivatableView<GridPane, Void> implements BsqBa
if (newValue != null) {
bondedRolesComboBox.getSelectionModel().clearSelection();
}
int lockupRows = 3;
if (newValue == LockupType.BONDED_ROLE) {
bondedRolesComboBox.setVisible(true);
bondedRolesLabel.setVisible(true);
lockupRows++;
} else {
bondedRolesComboBox.setVisible(false);
bondedRolesLabel.setVisible(false);
}
GridPane.setRowSpan(titledGroupBg, lockupRows);
GridPane.setRowIndex(lockupButton, GridPane.getRowIndex(amountInputTextField) + lockupRows);
};
//TODO handle trade type
lockupTypeComboBox.getSelectionModel().select(0);
bondedRolesComboBox = FormBuilder.<BondedRole>addComboBox(root, ++gridRow, Res.get("dao.bonding.lock.bondedRoles"));
bondedRolesComboBox = FormBuilder.addComboBox(root, ++gridRow, Res.get("dao.bonding.lock.bondedRoles"));
bondedRolesComboBox.setPromptText(Res.get("shared.select"));
bondedRolesComboBox.setConverter(new StringConverter<>() {
@Override
public String toString(BondedRole bondedRole) {
@ -171,10 +190,24 @@ public class LockupView extends ActivatableView<GridPane, Void> implements BsqBa
lockupButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.bonding.lock.lockupButton"));
lockupButton.setOnAction((event) -> {
bondingViewUtils.lockupBondForBondedRole(bondedRolesComboBox.getValue(),
() -> {
bondedRolesComboBox.getSelectionModel().clearSelection();
});
switch (lockupTypeComboBox.getValue()) {
case BONDED_ROLE:
if (bondedRolesComboBox.getValue() != null) {
bondingViewUtils.lockupBondForBondedRole(bondedRolesComboBox.getValue(),
() -> bondedRolesComboBox.getSelectionModel().clearSelection());
}
break;
case REPUTATION:
bondingViewUtils.lockupBondForReputation(bsqFormatter.parseToCoin(amountInputTextField.getText()),
Integer.parseInt(timeInputTextField.getText()),
() -> {
amountInputTextField.setText("");
timeInputTextField.setText("");
});
break;
default:
log.error("Unknown lockup option=" + lockupTypeComboBox.getValue());
}
});
focusOutListener = (observable, oldValue, newValue) -> {