mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-22 14:42:37 +01:00
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:
commit
06da45fb7a
9 changed files with 103 additions and 35 deletions
|
@ -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.RoleProposal;
|
||||||
import bisq.core.dao.state.model.governance.Vote;
|
import bisq.core.dao.state.model.governance.Vote;
|
||||||
import bisq.core.dao.state.storage.DaoStateStorageService;
|
import bisq.core.dao.state.storage.DaoStateStorageService;
|
||||||
|
import bisq.core.trade.DelayedPayoutAddressProvider;
|
||||||
|
|
||||||
import bisq.asset.Asset;
|
import bisq.asset.Asset;
|
||||||
|
|
||||||
|
@ -799,9 +800,9 @@ public class DaoFacade implements DaoSetupService {
|
||||||
if (Config.baseCurrencyNetwork().isMainnet()) {
|
if (Config.baseCurrencyNetwork().isMainnet()) {
|
||||||
// If Dao is deactivated we need to add the past addresses used as well.
|
// 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.
|
// This list need to be updated once a new address gets defined.
|
||||||
allPastParamValues.add("3EtUWqsGThPtjwUczw27YCo6EWvQdaPUyp"); // burning man 2019
|
allPastParamValues.add(DelayedPayoutAddressProvider.BM2019_ADDRESS);
|
||||||
allPastParamValues.add("3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV"); // burningman2
|
allPastParamValues.add(DelayedPayoutAddressProvider.BM2_ADDRESS);
|
||||||
allPastParamValues.add("34VLFgtFKAtwTdZ5rengTT2g2zC99sWQLC"); // burningman3 (https://github.com/bisq-network/roles/issues/80#issuecomment-723577776)
|
allPastParamValues.add(DelayedPayoutAddressProvider.BM3_ADDRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return allPastParamValues;
|
return allPastParamValues;
|
||||||
|
|
|
@ -18,11 +18,13 @@
|
||||||
package bisq.core.dao.state;
|
package bisq.core.dao.state;
|
||||||
|
|
||||||
import bisq.core.dao.DaoSetupService;
|
import bisq.core.dao.DaoSetupService;
|
||||||
|
import bisq.core.dao.governance.param.Param;
|
||||||
import bisq.core.dao.monitoring.DaoStateMonitoringService;
|
import bisq.core.dao.monitoring.DaoStateMonitoringService;
|
||||||
import bisq.core.dao.monitoring.model.DaoStateHash;
|
import bisq.core.dao.monitoring.model.DaoStateHash;
|
||||||
import bisq.core.dao.state.model.DaoState;
|
import bisq.core.dao.state.model.DaoState;
|
||||||
import bisq.core.dao.state.model.blockchain.Block;
|
import bisq.core.dao.state.model.blockchain.Block;
|
||||||
import bisq.core.dao.state.storage.DaoStateStorageService;
|
import bisq.core.dao.state.storage.DaoStateStorageService;
|
||||||
|
import bisq.core.trade.DelayedPayoutAddressProvider;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
|
@ -62,6 +64,7 @@ public class DaoStateSnapshotService implements DaoSetupService, DaoStateListene
|
||||||
private final DaoStateStorageService daoStateStorageService;
|
private final DaoStateStorageService daoStateStorageService;
|
||||||
private final DaoStateMonitoringService daoStateMonitoringService;
|
private final DaoStateMonitoringService daoStateMonitoringService;
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
|
private final Config config;
|
||||||
private final File storageDir;
|
private final File storageDir;
|
||||||
|
|
||||||
private protobuf.DaoState daoStateCandidate;
|
private protobuf.DaoState daoStateCandidate;
|
||||||
|
@ -86,12 +89,14 @@ public class DaoStateSnapshotService implements DaoSetupService, DaoStateListene
|
||||||
DaoStateStorageService daoStateStorageService,
|
DaoStateStorageService daoStateStorageService,
|
||||||
DaoStateMonitoringService daoStateMonitoringService,
|
DaoStateMonitoringService daoStateMonitoringService,
|
||||||
Preferences preferences,
|
Preferences preferences,
|
||||||
|
Config config,
|
||||||
@Named(Config.STORAGE_DIR) File storageDir) {
|
@Named(Config.STORAGE_DIR) File storageDir) {
|
||||||
this.daoStateService = daoStateService;
|
this.daoStateService = daoStateService;
|
||||||
this.genesisTxInfo = genesisTxInfo;
|
this.genesisTxInfo = genesisTxInfo;
|
||||||
this.daoStateStorageService = daoStateStorageService;
|
this.daoStateStorageService = daoStateStorageService;
|
||||||
this.daoStateMonitoringService = daoStateMonitoringService;
|
this.daoStateMonitoringService = daoStateMonitoringService;
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
|
this.config = config;
|
||||||
this.storageDir = storageDir;
|
this.storageDir = storageDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +119,20 @@ public class DaoStateSnapshotService implements DaoSetupService, DaoStateListene
|
||||||
// DaoStateListener
|
// 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 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.
|
// We need to listen during batch processing as well to write snapshots during that process.
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class CreateMakerFeeTx extends Task<PlaceOfferModel> {
|
||||||
|
|
||||||
TradeWalletService tradeWalletService = model.getTradeWalletService();
|
TradeWalletService tradeWalletService = model.getTradeWalletService();
|
||||||
|
|
||||||
String feeReceiver = FeeReceiverSelector.getAddress(model.getDaoFacade(), model.getFilterManager());
|
String feeReceiver = FeeReceiverSelector.getAddress(model.getFilterManager());
|
||||||
|
|
||||||
if (offer.isCurrencyForMakerFeeBtc()) {
|
if (offer.isCurrencyForMakerFeeBtc()) {
|
||||||
tradeWalletService.createBtcTradingFeeTx(
|
tradeWalletService.createBtcTradingFeeTx(
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -65,7 +65,7 @@ public class CreateTakerFeeTx extends TradeTask {
|
||||||
TradeWalletService tradeWalletService = processModel.getTradeWalletService();
|
TradeWalletService tradeWalletService = processModel.getTradeWalletService();
|
||||||
Transaction transaction;
|
Transaction transaction;
|
||||||
|
|
||||||
String feeReceiver = FeeReceiverSelector.getAddress(processModel.getDaoFacade(), processModel.getFilterManager());
|
String feeReceiver = FeeReceiverSelector.getAddress(processModel.getFilterManager());
|
||||||
|
|
||||||
if (trade.isCurrencyForTakerFeeBtc()) {
|
if (trade.isCurrencyForTakerFeeBtc()) {
|
||||||
transaction = tradeWalletService.createBtcTradingFeeTx(
|
transaction = tradeWalletService.createBtcTradingFeeTx(
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
package bisq.core.util;
|
package bisq.core.util;
|
||||||
|
|
||||||
import bisq.core.dao.DaoFacade;
|
|
||||||
import bisq.core.dao.governance.param.Param;
|
|
||||||
import bisq.core.filter.FilterManager;
|
import bisq.core.filter.FilterManager;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
@ -34,12 +32,18 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class FeeReceiverSelector {
|
public class FeeReceiverSelector {
|
||||||
public static String getAddress(DaoFacade daoFacade, FilterManager filterManager) {
|
public static final String BTC_FEE_RECEIVER_ADDRESS = "38bZBj5peYS3Husdz7AH3gEUiUbYRD951t";
|
||||||
return getAddress(daoFacade, filterManager, new Random());
|
|
||||||
|
public static String getMostRecentAddress() {
|
||||||
|
return BTC_FEE_RECEIVER_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getAddress(FilterManager filterManager) {
|
||||||
|
return getAddress(filterManager, new Random());
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static String getAddress(DaoFacade daoFacade, FilterManager filterManager, Random rnd) {
|
static String getAddress(FilterManager filterManager, Random rnd) {
|
||||||
List<String> feeReceivers = Optional.ofNullable(filterManager.getFilter())
|
List<String> feeReceivers = Optional.ofNullable(filterManager.getFilter())
|
||||||
.flatMap(f -> Optional.ofNullable(f.getBtcFeeReceiverAddresses()))
|
.flatMap(f -> Optional.ofNullable(f.getBtcFeeReceiverAddresses()))
|
||||||
.orElse(List.of());
|
.orElse(List.of());
|
||||||
|
@ -61,8 +65,8 @@ public class FeeReceiverSelector {
|
||||||
return receiverAddressList.get(weightedSelection(amountList, rnd));
|
return receiverAddressList.get(weightedSelection(amountList, rnd));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We keep default value as fallback in case no filter value is available or user has old version.
|
// If no fee address receiver is defined via filter we use the hard coded recent address
|
||||||
return daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS);
|
return getMostRecentAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
|
|
@ -39,6 +39,7 @@ public class DaoStateSnapshotServiceTest {
|
||||||
mock(DaoStateStorageService.class),
|
mock(DaoStateStorageService.class),
|
||||||
mock(DaoStateMonitoringService.class),
|
mock(DaoStateMonitoringService.class),
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,15 +19,17 @@ package bisq.core.provider.mempool;
|
||||||
|
|
||||||
import bisq.core.dao.governance.param.Param;
|
import bisq.core.dao.governance.param.Param;
|
||||||
import bisq.core.dao.state.DaoStateService;
|
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.ParsingUtils;
|
||||||
import bisq.core.util.coin.BsqFormatter;
|
import bisq.core.util.coin.BsqFormatter;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -43,8 +45,8 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
@ -61,11 +63,11 @@ public class TxValidatorTest {
|
||||||
btcFeeReceivers.add("13sxMq8mTw7CTSqgGiMPfwo6ZDsVYrHLmR");
|
btcFeeReceivers.add("13sxMq8mTw7CTSqgGiMPfwo6ZDsVYrHLmR");
|
||||||
btcFeeReceivers.add("19qA2BVPoyXDfHKVMovKG7SoxGY7xrBV8c");
|
btcFeeReceivers.add("19qA2BVPoyXDfHKVMovKG7SoxGY7xrBV8c");
|
||||||
btcFeeReceivers.add("19BNi5EpZhgBBWAt5ka7xWpJpX2ZWJEYyq");
|
btcFeeReceivers.add("19BNi5EpZhgBBWAt5ka7xWpJpX2ZWJEYyq");
|
||||||
btcFeeReceivers.add("38bZBj5peYS3Husdz7AH3gEUiUbYRD951t");
|
btcFeeReceivers.add(FeeReceiverSelector.BTC_FEE_RECEIVER_ADDRESS);
|
||||||
btcFeeReceivers.add("3EtUWqsGThPtjwUczw27YCo6EWvQdaPUyp");
|
btcFeeReceivers.add(DelayedPayoutAddressProvider.BM2019_ADDRESS);
|
||||||
btcFeeReceivers.add("1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7");
|
btcFeeReceivers.add("1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7");
|
||||||
btcFeeReceivers.add("3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV");
|
btcFeeReceivers.add("3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV");
|
||||||
btcFeeReceivers.add("34VLFgtFKAtwTdZ5rengTT2g2zC99sWQLC");
|
btcFeeReceivers.add(DelayedPayoutAddressProvider.BM3_ADDRESS);
|
||||||
log.warn("Known BTC fee receivers: {}", btcFeeReceivers.toString());
|
log.warn("Known BTC fee receivers: {}", btcFeeReceivers.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
package bisq.core.util;
|
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.Filter;
|
||||||
import bisq.core.filter.FilterManager;
|
import bisq.core.filter.FilterManager;
|
||||||
|
|
||||||
|
@ -42,8 +40,6 @@ import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class FeeReceiverSelectorTest {
|
public class FeeReceiverSelectorTest {
|
||||||
@Mock
|
|
||||||
private DaoFacade daoFacade;
|
|
||||||
@Mock
|
@Mock
|
||||||
private FilterManager filterManager;
|
private FilterManager filterManager;
|
||||||
|
|
||||||
|
@ -55,7 +51,7 @@ public class FeeReceiverSelectorTest {
|
||||||
|
|
||||||
Map<String, Integer> selectionCounts = new HashMap<>();
|
Map<String, Integer> selectionCounts = new HashMap<>();
|
||||||
for (int i = 0; i < 400; i++) {
|
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);
|
selectionCounts.compute(address, (k, n) -> n != null ? n + 1 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,34 +65,26 @@ public class FeeReceiverSelectorTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAddress_noValidReceivers_nullFilter() {
|
public void testGetAddress_noValidReceivers_nullFilter() {
|
||||||
when(daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS)).thenReturn("default");
|
|
||||||
|
|
||||||
when(filterManager.getFilter()).thenReturn(null);
|
when(filterManager.getFilter()).thenReturn(null);
|
||||||
assertEquals("default", FeeReceiverSelector.getAddress(daoFacade, filterManager));
|
assertEquals(FeeReceiverSelector.getMostRecentAddress(), FeeReceiverSelector.getAddress(filterManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAddress_noValidReceivers_filterWithNullList() {
|
public void testGetAddress_noValidReceivers_filterWithNullList() {
|
||||||
when(daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS)).thenReturn("default");
|
|
||||||
|
|
||||||
when(filterManager.getFilter()).thenReturn(filterWithReceivers(null));
|
when(filterManager.getFilter()).thenReturn(filterWithReceivers(null));
|
||||||
assertEquals("default", FeeReceiverSelector.getAddress(daoFacade, filterManager));
|
assertEquals(FeeReceiverSelector.getMostRecentAddress(), FeeReceiverSelector.getAddress(filterManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAddress_noValidReceivers_filterWithEmptyList() {
|
public void testGetAddress_noValidReceivers_filterWithEmptyList() {
|
||||||
when(daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS)).thenReturn("default");
|
|
||||||
|
|
||||||
when(filterManager.getFilter()).thenReturn(filterWithReceivers(List.of()));
|
when(filterManager.getFilter()).thenReturn(filterWithReceivers(List.of()));
|
||||||
assertEquals("default", FeeReceiverSelector.getAddress(daoFacade, filterManager));
|
assertEquals(FeeReceiverSelector.getMostRecentAddress(), FeeReceiverSelector.getAddress(filterManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAddress_noValidReceivers_filterWithIllFormedList() {
|
public void testGetAddress_noValidReceivers_filterWithIllFormedList() {
|
||||||
when(daoFacade.getParamValue(Param.RECIPIENT_BTC_ADDRESS)).thenReturn("default");
|
|
||||||
|
|
||||||
when(filterManager.getFilter()).thenReturn(filterWithReceivers(List.of("ill-formed")));
|
when(filterManager.getFilter()).thenReturn(filterWithReceivers(List.of("ill-formed")));
|
||||||
assertEquals("default", FeeReceiverSelector.getAddress(daoFacade, filterManager));
|
assertEquals(FeeReceiverSelector.getMostRecentAddress(), FeeReceiverSelector.getAddress(filterManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Add table
Reference in a new issue