Merge pull request #5764 from chimp1984/avoid-usage-of-outdated-addresses

Avoid that outdated donation and fee addresses are used.
This commit is contained in:
Christoph Atteneder 2021-11-09 21:01:47 +01:00 committed by GitHub
commit 06da45fb7a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 103 additions and 35 deletions

View file

@ -73,6 +73,7 @@ import bisq.core.dao.state.model.governance.Role;
import bisq.core.dao.state.model.governance.RoleProposal;
import bisq.core.dao.state.model.governance.Vote;
import bisq.core.dao.state.storage.DaoStateStorageService;
import bisq.core.trade.DelayedPayoutAddressProvider;
import bisq.asset.Asset;
@ -799,9 +800,9 @@ public class DaoFacade implements DaoSetupService {
if (Config.baseCurrencyNetwork().isMainnet()) {
// If Dao is deactivated we need to add the past addresses used as well.
// This list need to be updated once a new address gets defined.
allPastParamValues.add("3EtUWqsGThPtjwUczw27YCo6EWvQdaPUyp"); // burning man 2019
allPastParamValues.add("3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV"); // burningman2
allPastParamValues.add("34VLFgtFKAtwTdZ5rengTT2g2zC99sWQLC"); // burningman3 (https://github.com/bisq-network/roles/issues/80#issuecomment-723577776)
allPastParamValues.add(DelayedPayoutAddressProvider.BM2019_ADDRESS);
allPastParamValues.add(DelayedPayoutAddressProvider.BM2_ADDRESS);
allPastParamValues.add(DelayedPayoutAddressProvider.BM3_ADDRESS);
}
return allPastParamValues;

View file

@ -18,11 +18,13 @@
package bisq.core.dao.state;
import bisq.core.dao.DaoSetupService;
import bisq.core.dao.governance.param.Param;
import bisq.core.dao.monitoring.DaoStateMonitoringService;
import bisq.core.dao.monitoring.model.DaoStateHash;
import bisq.core.dao.state.model.DaoState;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.storage.DaoStateStorageService;
import bisq.core.trade.DelayedPayoutAddressProvider;
import bisq.core.user.Preferences;
import bisq.common.config.Config;
@ -62,6 +64,7 @@ public class DaoStateSnapshotService implements DaoSetupService, DaoStateListene
private final DaoStateStorageService daoStateStorageService;
private final DaoStateMonitoringService daoStateMonitoringService;
private final Preferences preferences;
private final Config config;
private final File storageDir;
private protobuf.DaoState daoStateCandidate;
@ -86,12 +89,14 @@ public class DaoStateSnapshotService implements DaoSetupService, DaoStateListene
DaoStateStorageService daoStateStorageService,
DaoStateMonitoringService daoStateMonitoringService,
Preferences preferences,
Config config,
@Named(Config.STORAGE_DIR) File storageDir) {
this.daoStateService = daoStateService;
this.genesisTxInfo = genesisTxInfo;
this.daoStateStorageService = daoStateStorageService;
this.daoStateMonitoringService = daoStateMonitoringService;
this.preferences = preferences;
this.config = config;
this.storageDir = storageDir;
}
@ -114,6 +119,20 @@ public class DaoStateSnapshotService implements DaoSetupService, DaoStateListene
// DaoStateListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onParseBlockCompleteAfterBatchProcessing(Block block) {
if (config.baseCurrencyNetwork.isMainnet()) {
// In case the DAO state is invalid we might get an outdated RECIPIENT_BTC_ADDRESS. In that case we trigger
// a dao resync from resources.
String address = daoStateService.getParamValue(Param.RECIPIENT_BTC_ADDRESS, daoStateService.getChainHeight());
if (DelayedPayoutAddressProvider.isOutdatedAddress(address)) {
log.warn("The RECIPIENT_BTC_ADDRESS is not as expected. The DAO state is probably out of " +
"sync and a resync should fix that issue.");
resyncDaoStateFromResources();
}
}
}
// We listen onDaoStateChanged to ensure the dao state has been processed from listener clients after parsing.
// We need to listen during batch processing as well to write snapshots during that process.
@Override

View file

@ -66,7 +66,7 @@ public class CreateMakerFeeTx extends Task<PlaceOfferModel> {
TradeWalletService tradeWalletService = model.getTradeWalletService();
String feeReceiver = FeeReceiverSelector.getAddress(model.getDaoFacade(), model.getFilterManager());
String feeReceiver = FeeReceiverSelector.getAddress(model.getFilterManager());
if (offer.isCurrencyForMakerFeeBtc()) {
tradeWalletService.createBtcTradingFeeTx(

View file

@ -0,0 +1,53 @@
/*
* 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.trade;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.governance.param.Param;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DelayedPayoutAddressProvider {
public static final String INITIAL_BM_ADDRESS = "1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7"; // Initial DAO donation address
public static final String BM2019_ADDRESS = "3EtUWqsGThPtjwUczw27YCo6EWvQdaPUyp"; // burning2019
public static final String BM2_ADDRESS = "3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV"; // burningman2
// burningman3 https://github.com/bisq-network/roles/issues/80#issuecomment-723577776
public static final String BM3_ADDRESS = "34VLFgtFKAtwTdZ5rengTT2g2zC99sWQLC";
public static String getDelayedPayoutAddress(DaoFacade daoFacade) {
String address = daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS);
if (isOutdatedAddress(address)) {
log.warn("Outdated delayed payout address. " +
"This can be the case if the DAO is deactivated or if the user has an invalid DAO state." +
"We set the address to the recent one (BM3_ADDRESS).");
return getAddress();
}
return address;
}
public static boolean isOutdatedAddress(String address) {
return address.equals(INITIAL_BM_ADDRESS) ||
address.equals(BM2019_ADDRESS) ||
address.equals(BM2_ADDRESS);
}
public static String getAddress() {
return BM3_ADDRESS;
}
}

View file

@ -65,7 +65,7 @@ public class CreateTakerFeeTx extends TradeTask {
TradeWalletService tradeWalletService = processModel.getTradeWalletService();
Transaction transaction;
String feeReceiver = FeeReceiverSelector.getAddress(processModel.getDaoFacade(), processModel.getFilterManager());
String feeReceiver = FeeReceiverSelector.getAddress(processModel.getFilterManager());
if (trade.isCurrencyForTakerFeeBtc()) {
transaction = tradeWalletService.createBtcTradingFeeTx(

View file

@ -17,8 +17,6 @@
package bisq.core.util;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.governance.param.Param;
import bisq.core.filter.FilterManager;
import org.bitcoinj.core.Coin;
@ -34,12 +32,18 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class FeeReceiverSelector {
public static String getAddress(DaoFacade daoFacade, FilterManager filterManager) {
return getAddress(daoFacade, filterManager, new Random());
public static final String BTC_FEE_RECEIVER_ADDRESS = "38bZBj5peYS3Husdz7AH3gEUiUbYRD951t";
public static String getMostRecentAddress() {
return BTC_FEE_RECEIVER_ADDRESS;
}
public static String getAddress(FilterManager filterManager) {
return getAddress(filterManager, new Random());
}
@VisibleForTesting
static String getAddress(DaoFacade daoFacade, FilterManager filterManager, Random rnd) {
static String getAddress(FilterManager filterManager, Random rnd) {
List<String> feeReceivers = Optional.ofNullable(filterManager.getFilter())
.flatMap(f -> Optional.ofNullable(f.getBtcFeeReceiverAddresses()))
.orElse(List.of());
@ -61,8 +65,8 @@ public class FeeReceiverSelector {
return receiverAddressList.get(weightedSelection(amountList, rnd));
}
// We keep default value as fallback in case no filter value is available or user has old version.
return daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS);
// If no fee address receiver is defined via filter we use the hard coded recent address
return getMostRecentAddress();
}
@VisibleForTesting

View file

@ -39,6 +39,7 @@ public class DaoStateSnapshotServiceTest {
mock(DaoStateStorageService.class),
mock(DaoStateMonitoringService.class),
null,
null,
null);
}

View file

@ -19,15 +19,17 @@ package bisq.core.provider.mempool;
import bisq.core.dao.governance.param.Param;
import bisq.core.dao.state.DaoStateService;
import bisq.core.trade.DelayedPayoutAddressProvider;
import bisq.core.util.FeeReceiverSelector;
import bisq.core.util.ParsingUtils;
import bisq.core.util.coin.BsqFormatter;
import org.bitcoinj.core.Coin;
import com.google.gson.Gson;
import org.apache.commons.io.IOUtils;
import org.bitcoinj.core.Coin;
import java.io.IOException;
import java.util.ArrayList;
@ -43,8 +45,8 @@ import org.slf4j.LoggerFactory;
import org.mockito.Mockito;
import org.mockito.stubbing.Answer;
import org.junit.Test;
import org.junit.Assert;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -61,11 +63,11 @@ public class TxValidatorTest {
btcFeeReceivers.add("13sxMq8mTw7CTSqgGiMPfwo6ZDsVYrHLmR");
btcFeeReceivers.add("19qA2BVPoyXDfHKVMovKG7SoxGY7xrBV8c");
btcFeeReceivers.add("19BNi5EpZhgBBWAt5ka7xWpJpX2ZWJEYyq");
btcFeeReceivers.add("38bZBj5peYS3Husdz7AH3gEUiUbYRD951t");
btcFeeReceivers.add("3EtUWqsGThPtjwUczw27YCo6EWvQdaPUyp");
btcFeeReceivers.add(FeeReceiverSelector.BTC_FEE_RECEIVER_ADDRESS);
btcFeeReceivers.add(DelayedPayoutAddressProvider.BM2019_ADDRESS);
btcFeeReceivers.add("1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7");
btcFeeReceivers.add("3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV");
btcFeeReceivers.add("34VLFgtFKAtwTdZ5rengTT2g2zC99sWQLC");
btcFeeReceivers.add(DelayedPayoutAddressProvider.BM3_ADDRESS);
log.warn("Known BTC fee receivers: {}", btcFeeReceivers.toString());
}

View file

@ -17,8 +17,6 @@
package bisq.core.util;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.governance.param.Param;
import bisq.core.filter.Filter;
import bisq.core.filter.FilterManager;
@ -42,8 +40,6 @@ import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class FeeReceiverSelectorTest {
@Mock
private DaoFacade daoFacade;
@Mock
private FilterManager filterManager;
@ -55,7 +51,7 @@ public class FeeReceiverSelectorTest {
Map<String, Integer> selectionCounts = new HashMap<>();
for (int i = 0; i < 400; i++) {
String address = FeeReceiverSelector.getAddress(daoFacade, filterManager, rnd);
String address = FeeReceiverSelector.getAddress(filterManager, rnd);
selectionCounts.compute(address, (k, n) -> n != null ? n + 1 : 1);
}
@ -69,34 +65,26 @@ public class FeeReceiverSelectorTest {
@Test
public void testGetAddress_noValidReceivers_nullFilter() {
when(daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS)).thenReturn("default");
when(filterManager.getFilter()).thenReturn(null);
assertEquals("default", FeeReceiverSelector.getAddress(daoFacade, filterManager));
assertEquals(FeeReceiverSelector.getMostRecentAddress(), FeeReceiverSelector.getAddress(filterManager));
}
@Test
public void testGetAddress_noValidReceivers_filterWithNullList() {
when(daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS)).thenReturn("default");
when(filterManager.getFilter()).thenReturn(filterWithReceivers(null));
assertEquals("default", FeeReceiverSelector.getAddress(daoFacade, filterManager));
assertEquals(FeeReceiverSelector.getMostRecentAddress(), FeeReceiverSelector.getAddress(filterManager));
}
@Test
public void testGetAddress_noValidReceivers_filterWithEmptyList() {
when(daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS)).thenReturn("default");
when(filterManager.getFilter()).thenReturn(filterWithReceivers(List.of()));
assertEquals("default", FeeReceiverSelector.getAddress(daoFacade, filterManager));
assertEquals(FeeReceiverSelector.getMostRecentAddress(), FeeReceiverSelector.getAddress(filterManager));
}
@Test
public void testGetAddress_noValidReceivers_filterWithIllFormedList() {
when(daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS)).thenReturn("default");
when(filterManager.getFilter()).thenReturn(filterWithReceivers(List.of("ill-formed")));
assertEquals("default", FeeReceiverSelector.getAddress(daoFacade, filterManager));
assertEquals(FeeReceiverSelector.getMostRecentAddress(), FeeReceiverSelector.getAddress(filterManager));
}
@Test