mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-07-26 16:35:18 -04:00
Bisq
This commit is contained in:
commit
8a38081c04
2800 changed files with 344130 additions and 0 deletions
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
* 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.account.sign;
|
||||
|
||||
|
||||
import bisq.core.account.witness.AccountAgeWitness;
|
||||
import bisq.core.filter.FilterManager;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||
import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService;
|
||||
|
||||
import bisq.common.crypto.CryptoException;
|
||||
import bisq.common.crypto.KeyRing;
|
||||
import bisq.common.crypto.Sig;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import java.security.KeyPair;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static bisq.core.account.sign.SignedWitness.VerificationMethod.ARBITRATOR;
|
||||
import static bisq.core.account.sign.SignedWitness.VerificationMethod.TRADE;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class SignedWitnessServiceTest {
|
||||
private SignedWitnessService signedWitnessService;
|
||||
private byte[] account1DataHash;
|
||||
private byte[] account2DataHash;
|
||||
private byte[] account3DataHash;
|
||||
private AccountAgeWitness aew1;
|
||||
private AccountAgeWitness aew2;
|
||||
private AccountAgeWitness aew3;
|
||||
private byte[] signature1;
|
||||
private byte[] signature2;
|
||||
private byte[] signature3;
|
||||
private byte[] signer1PubKey;
|
||||
private byte[] signer2PubKey;
|
||||
private byte[] signer3PubKey;
|
||||
private byte[] witnessOwner1PubKey;
|
||||
private byte[] witnessOwner2PubKey;
|
||||
private byte[] witnessOwner3PubKey;
|
||||
private long date1;
|
||||
private long date2;
|
||||
private long date3;
|
||||
private long tradeAmount1;
|
||||
private long tradeAmount2;
|
||||
private long tradeAmount3;
|
||||
private long SIGN_AGE_1 = SignedWitnessService.SIGNER_AGE_DAYS * 3 + 5;
|
||||
private long SIGN_AGE_2 = SignedWitnessService.SIGNER_AGE_DAYS * 2 + 4;
|
||||
private long SIGN_AGE_3 = SignedWitnessService.SIGNER_AGE_DAYS + 3;
|
||||
private KeyRing keyRing;
|
||||
private P2PService p2pService;
|
||||
private FilterManager filterManager;
|
||||
private ECKey arbitrator1Key;
|
||||
KeyPair peer1KeyPair;
|
||||
KeyPair peer2KeyPair;
|
||||
KeyPair peer3KeyPair;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
AppendOnlyDataStoreService appendOnlyDataStoreService = mock(AppendOnlyDataStoreService.class);
|
||||
ArbitratorManager arbitratorManager = mock(ArbitratorManager.class);
|
||||
when(arbitratorManager.isPublicKeyInList(any())).thenReturn(true);
|
||||
keyRing = mock(KeyRing.class);
|
||||
p2pService = mock(P2PService.class);
|
||||
filterManager = mock(FilterManager.class);
|
||||
signedWitnessService = new SignedWitnessService(keyRing, p2pService, arbitratorManager, null, appendOnlyDataStoreService, null, filterManager);
|
||||
account1DataHash = org.bitcoinj.core.Utils.sha256hash160(new byte[]{1});
|
||||
account2DataHash = org.bitcoinj.core.Utils.sha256hash160(new byte[]{2});
|
||||
account3DataHash = org.bitcoinj.core.Utils.sha256hash160(new byte[]{3});
|
||||
long account1CreationTime = getTodayMinusNDays(SIGN_AGE_1 + 1);
|
||||
long account2CreationTime = getTodayMinusNDays(SIGN_AGE_2 + 1);
|
||||
long account3CreationTime = getTodayMinusNDays(SIGN_AGE_3 + 1);
|
||||
aew1 = new AccountAgeWitness(account1DataHash, account1CreationTime);
|
||||
aew2 = new AccountAgeWitness(account2DataHash, account2CreationTime);
|
||||
aew3 = new AccountAgeWitness(account3DataHash, account3CreationTime);
|
||||
arbitrator1Key = new ECKey();
|
||||
peer1KeyPair = Sig.generateKeyPair();
|
||||
peer2KeyPair = Sig.generateKeyPair();
|
||||
peer3KeyPair = Sig.generateKeyPair();
|
||||
signature1 = arbitrator1Key.signMessage(Utilities.encodeToHex(account1DataHash)).getBytes(Charsets.UTF_8);
|
||||
signature2 = Sig.sign(peer1KeyPair.getPrivate(), Utilities.encodeToHex(account2DataHash).getBytes(Charsets.UTF_8));
|
||||
signature3 = Sig.sign(peer2KeyPair.getPrivate(), Utilities.encodeToHex(account3DataHash).getBytes(Charsets.UTF_8));
|
||||
date1 = getTodayMinusNDays(SIGN_AGE_1);
|
||||
date2 = getTodayMinusNDays(SIGN_AGE_2);
|
||||
date3 = getTodayMinusNDays(SIGN_AGE_3);
|
||||
signer1PubKey = arbitrator1Key.getPubKey();
|
||||
signer2PubKey = Sig.getPublicKeyBytes(peer1KeyPair.getPublic());
|
||||
signer3PubKey = Sig.getPublicKeyBytes(peer2KeyPair.getPublic());
|
||||
witnessOwner1PubKey = Sig.getPublicKeyBytes(peer1KeyPair.getPublic());
|
||||
witnessOwner2PubKey = Sig.getPublicKeyBytes(peer2KeyPair.getPublic());
|
||||
witnessOwner3PubKey = Sig.getPublicKeyBytes(peer3KeyPair.getPublic());
|
||||
tradeAmount1 = 1000;
|
||||
tradeAmount2 = 1001;
|
||||
tradeAmount3 = 1001;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidAccountAgeWitnessOk() {
|
||||
SignedWitness sw1 = new SignedWitness(ARBITRATOR, account1DataHash, signature1, signer1PubKey, witnessOwner1PubKey, date1, tradeAmount1);
|
||||
SignedWitness sw2 = new SignedWitness(TRADE, account2DataHash, signature2, signer2PubKey, witnessOwner2PubKey, date2, tradeAmount2);
|
||||
SignedWitness sw3 = new SignedWitness(TRADE, account3DataHash, signature3, signer3PubKey, witnessOwner3PubKey, date3, tradeAmount3);
|
||||
|
||||
signedWitnessService.addToMap(sw1);
|
||||
signedWitnessService.addToMap(sw2);
|
||||
signedWitnessService.addToMap(sw3);
|
||||
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidAccountAgeWitnessArbitratorSignatureProblem() {
|
||||
signature1 = new byte[]{1, 2, 3};
|
||||
|
||||
SignedWitness sw1 = new SignedWitness(ARBITRATOR, account1DataHash, signature1, signer1PubKey, witnessOwner1PubKey, date1, tradeAmount1);
|
||||
SignedWitness sw2 = new SignedWitness(TRADE, account2DataHash, signature2, signer2PubKey, witnessOwner2PubKey, date2, tradeAmount2);
|
||||
SignedWitness sw3 = new SignedWitness(TRADE, account3DataHash, signature3, signer3PubKey, witnessOwner3PubKey, date3, tradeAmount3);
|
||||
|
||||
signedWitnessService.addToMap(sw1);
|
||||
signedWitnessService.addToMap(sw2);
|
||||
signedWitnessService.addToMap(sw3);
|
||||
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidAccountAgeWitnessPeerSignatureProblem() {
|
||||
signature2 = new byte[]{1, 2, 3};
|
||||
|
||||
SignedWitness sw1 = new SignedWitness(ARBITRATOR, account1DataHash, signature1, signer1PubKey, witnessOwner1PubKey, date1, tradeAmount1);
|
||||
SignedWitness sw2 = new SignedWitness(TRADE, account2DataHash, signature2, signer2PubKey, witnessOwner2PubKey, date2, tradeAmount2);
|
||||
SignedWitness sw3 = new SignedWitness(TRADE, account3DataHash, signature3, signer3PubKey, witnessOwner3PubKey, date3, tradeAmount3);
|
||||
|
||||
signedWitnessService.addToMap(sw1);
|
||||
signedWitnessService.addToMap(sw2);
|
||||
signedWitnessService.addToMap(sw3);
|
||||
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidSelfSignatureOk() throws Exception {
|
||||
KeyPair peer1KeyPair = Sig.generateKeyPair();
|
||||
signer2PubKey = Sig.getPublicKeyBytes(peer1KeyPair.getPublic());
|
||||
|
||||
signature2 = Sig.sign(peer1KeyPair.getPrivate(), Utilities.encodeToHex(account2DataHash).getBytes(Charsets.UTF_8));
|
||||
signature3 = Sig.sign(peer1KeyPair.getPrivate(), Utilities.encodeToHex(account3DataHash).getBytes(Charsets.UTF_8));
|
||||
|
||||
SignedWitness sw1 = new SignedWitness(ARBITRATOR, account1DataHash, signature1, signer1PubKey, signer2PubKey, date1, tradeAmount1);
|
||||
SignedWitness sw2 = new SignedWitness(TRADE, account2DataHash, signature2, signer2PubKey, signer2PubKey, date2, tradeAmount2);
|
||||
SignedWitness sw3 = new SignedWitness(TRADE, account3DataHash, signature3, signer2PubKey, signer2PubKey, date3, tradeAmount3);
|
||||
|
||||
signedWitnessService.addToMap(sw1);
|
||||
signedWitnessService.addToMap(sw2);
|
||||
signedWitnessService.addToMap(sw3);
|
||||
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidSimpleLoopSignatureProblem() throws Exception {
|
||||
// A reasonable case where user1 is signed by user2 and later switches account and the new
|
||||
// account gets signed by user2. This is not allowed.
|
||||
KeyPair peer1KeyPair = Sig.generateKeyPair();
|
||||
KeyPair peer2KeyPair = Sig.generateKeyPair();
|
||||
byte[] user1PubKey = Sig.getPublicKeyBytes(peer1KeyPair.getPublic());
|
||||
byte[] user2PubKey = Sig.getPublicKeyBytes(peer2KeyPair.getPublic());
|
||||
|
||||
signature2 = Sig.sign(peer1KeyPair.getPrivate(), Utilities.encodeToHex(account2DataHash).getBytes(Charsets.UTF_8));
|
||||
signature3 = Sig.sign(peer2KeyPair.getPrivate(), Utilities.encodeToHex(account3DataHash).getBytes(Charsets.UTF_8));
|
||||
|
||||
SignedWitness sw1 = new SignedWitness(ARBITRATOR, account1DataHash, signature1, signer1PubKey, user1PubKey, date1, tradeAmount1);
|
||||
SignedWitness sw2 = new SignedWitness(TRADE, account2DataHash, signature2, user1PubKey, user2PubKey, date2, tradeAmount2);
|
||||
SignedWitness sw3 = new SignedWitness(TRADE, account3DataHash, signature3, user2PubKey, user1PubKey, date3, tradeAmount3);
|
||||
|
||||
signedWitnessService.addToMap(sw1);
|
||||
signedWitnessService.addToMap(sw2);
|
||||
signedWitnessService.addToMap(sw3);
|
||||
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidAccountAgeWitnessDateTooSoonProblem() {
|
||||
date3 = getTodayMinusNDays(SIGN_AGE_2 - 1);
|
||||
|
||||
SignedWitness sw1 = new SignedWitness(ARBITRATOR, account1DataHash, signature1, signer1PubKey, witnessOwner1PubKey, date1, tradeAmount1);
|
||||
SignedWitness sw2 = new SignedWitness(TRADE, account2DataHash, signature2, signer2PubKey, witnessOwner2PubKey, date2, tradeAmount2);
|
||||
SignedWitness sw3 = new SignedWitness(TRADE, account3DataHash, signature3, signer3PubKey, witnessOwner3PubKey, date3, tradeAmount3);
|
||||
|
||||
signedWitnessService.addToMap(sw1);
|
||||
signedWitnessService.addToMap(sw2);
|
||||
signedWitnessService.addToMap(sw3);
|
||||
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidAccountAgeWitnessDateTooLateProblem() {
|
||||
date3 = getTodayMinusNDays(3);
|
||||
|
||||
SignedWitness sw1 = new SignedWitness(ARBITRATOR, account1DataHash, signature1, signer1PubKey, witnessOwner1PubKey, date1, tradeAmount1);
|
||||
SignedWitness sw2 = new SignedWitness(TRADE, account2DataHash, signature2, signer2PubKey, witnessOwner2PubKey, date2, tradeAmount2);
|
||||
SignedWitness sw3 = new SignedWitness(TRADE, account3DataHash, signature3, signer3PubKey, witnessOwner3PubKey, date3, tradeAmount3);
|
||||
|
||||
signedWitnessService.addToMap(sw1);
|
||||
signedWitnessService.addToMap(sw2);
|
||||
signedWitnessService.addToMap(sw3);
|
||||
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIsValidAccountAgeWitnessEndlessLoop() throws Exception {
|
||||
byte[] account1DataHash = org.bitcoinj.core.Utils.sha256hash160(new byte[]{1});
|
||||
byte[] account2DataHash = org.bitcoinj.core.Utils.sha256hash160(new byte[]{2});
|
||||
byte[] account3DataHash = org.bitcoinj.core.Utils.sha256hash160(new byte[]{3});
|
||||
long account1CreationTime = getTodayMinusNDays(SIGN_AGE_1 + 1);
|
||||
long account2CreationTime = getTodayMinusNDays(SIGN_AGE_2 + 1);
|
||||
long account3CreationTime = getTodayMinusNDays(SIGN_AGE_3 + 1);
|
||||
AccountAgeWitness aew1 = new AccountAgeWitness(account1DataHash, account1CreationTime);
|
||||
AccountAgeWitness aew2 = new AccountAgeWitness(account2DataHash, account2CreationTime);
|
||||
AccountAgeWitness aew3 = new AccountAgeWitness(account3DataHash, account3CreationTime);
|
||||
|
||||
KeyPair peer1KeyPair = Sig.generateKeyPair();
|
||||
KeyPair peer2KeyPair = Sig.generateKeyPair();
|
||||
KeyPair peer3KeyPair = Sig.generateKeyPair();
|
||||
|
||||
String account1DataHashAsHexString = Utilities.encodeToHex(account1DataHash);
|
||||
String account2DataHashAsHexString = Utilities.encodeToHex(account2DataHash);
|
||||
String account3DataHashAsHexString = Utilities.encodeToHex(account3DataHash);
|
||||
|
||||
byte[] signature1 = Sig.sign(peer3KeyPair.getPrivate(), account1DataHashAsHexString.getBytes(Charsets.UTF_8));
|
||||
byte[] signature2 = Sig.sign(peer1KeyPair.getPrivate(), account2DataHashAsHexString.getBytes(Charsets.UTF_8));
|
||||
byte[] signature3 = Sig.sign(peer2KeyPair.getPrivate(), account3DataHashAsHexString.getBytes(Charsets.UTF_8));
|
||||
|
||||
byte[] signer1PubKey = Sig.getPublicKeyBytes(peer3KeyPair.getPublic());
|
||||
byte[] signer2PubKey = Sig.getPublicKeyBytes(peer1KeyPair.getPublic());
|
||||
byte[] signer3PubKey = Sig.getPublicKeyBytes(peer2KeyPair.getPublic());
|
||||
byte[] witnessOwner1PubKey = Sig.getPublicKeyBytes(peer1KeyPair.getPublic());
|
||||
byte[] witnessOwner2PubKey = Sig.getPublicKeyBytes(peer2KeyPair.getPublic());
|
||||
byte[] witnessOwner3PubKey = Sig.getPublicKeyBytes(peer3KeyPair.getPublic());
|
||||
long date1 = getTodayMinusNDays(SIGN_AGE_1);
|
||||
long date2 = getTodayMinusNDays(SIGN_AGE_2);
|
||||
long date3 = getTodayMinusNDays(SIGN_AGE_3);
|
||||
|
||||
long tradeAmount1 = 1000;
|
||||
long tradeAmount2 = 1001;
|
||||
long tradeAmount3 = 1001;
|
||||
|
||||
SignedWitness sw1 = new SignedWitness(TRADE, account1DataHash, signature1, signer1PubKey, witnessOwner1PubKey, date1, tradeAmount1);
|
||||
SignedWitness sw2 = new SignedWitness(TRADE, account2DataHash, signature2, signer2PubKey, witnessOwner2PubKey, date2, tradeAmount2);
|
||||
SignedWitness sw3 = new SignedWitness(TRADE, account3DataHash, signature3, signer3PubKey, witnessOwner3PubKey, date3, tradeAmount3);
|
||||
|
||||
signedWitnessService.addToMap(sw1);
|
||||
signedWitnessService.addToMap(sw2);
|
||||
signedWitnessService.addToMap(sw3);
|
||||
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIsValidAccountAgeWitnessLongLoop() throws Exception {
|
||||
AccountAgeWitness aew = null;
|
||||
KeyPair signerKeyPair;
|
||||
KeyPair signedKeyPair = Sig.generateKeyPair();
|
||||
int iterations = 1002;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
byte[] accountDataHash = org.bitcoinj.core.Utils.sha256hash160(String.valueOf(i).getBytes(Charsets.UTF_8));
|
||||
long accountCreationTime = getTodayMinusNDays((iterations - i) * (SignedWitnessService.SIGNER_AGE_DAYS + 1));
|
||||
aew = new AccountAgeWitness(accountDataHash, accountCreationTime);
|
||||
String accountDataHashAsHexString = Utilities.encodeToHex(accountDataHash);
|
||||
byte[] signature;
|
||||
byte[] signerPubKey;
|
||||
if (i == 0) {
|
||||
// use arbitrator key
|
||||
ECKey arbitratorKey = new ECKey();
|
||||
signedKeyPair = Sig.generateKeyPair();
|
||||
String signature1String = arbitratorKey.signMessage(accountDataHashAsHexString);
|
||||
signature = signature1String.getBytes(Charsets.UTF_8);
|
||||
signerPubKey = arbitratorKey.getPubKey();
|
||||
} else {
|
||||
signerKeyPair = signedKeyPair;
|
||||
signedKeyPair = Sig.generateKeyPair();
|
||||
signature = Sig.sign(signedKeyPair.getPrivate(), accountDataHashAsHexString.getBytes(Charsets.UTF_8));
|
||||
signerPubKey = Sig.getPublicKeyBytes(signerKeyPair.getPublic());
|
||||
}
|
||||
byte[] witnessOwnerPubKey = Sig.getPublicKeyBytes(signedKeyPair.getPublic());
|
||||
long date = getTodayMinusNDays((iterations - i) * (SignedWitnessService.SIGNER_AGE_DAYS + 1));
|
||||
SignedWitness sw = new SignedWitness(i == 0 ? ARBITRATOR : TRADE, accountDataHash, signature, signerPubKey, witnessOwnerPubKey, date, tradeAmount1);
|
||||
signedWitnessService.addToMap(sw);
|
||||
}
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew));
|
||||
}
|
||||
|
||||
|
||||
private long getTodayMinusNDays(long days) {
|
||||
return Instant.ofEpochMilli(new Date().getTime()).minus(days, ChronoUnit.DAYS).toEpochMilli();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignAccountAgeWitness_withTooLowTradeAmount() throws CryptoException {
|
||||
long accountCreationTime = getTodayMinusNDays(SIGN_AGE_1 + 1);
|
||||
|
||||
KeyPair peerKeyPair = Sig.generateKeyPair();
|
||||
KeyPair signerKeyPair = Sig.generateKeyPair();
|
||||
|
||||
when(keyRing.getSignatureKeyPair()).thenReturn(signerKeyPair);
|
||||
|
||||
AccountAgeWitness accountAgeWitness = new AccountAgeWitness(account1DataHash, accountCreationTime);
|
||||
signedWitnessService.signAndPublishAccountAgeWitness(Coin.ZERO, accountAgeWitness, peerKeyPair.getPublic());
|
||||
|
||||
verify(p2pService, never()).addPersistableNetworkPayload(any(PersistableNetworkPayload.class), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignAccountAgeWitness_withSufficientTradeAmount() throws CryptoException {
|
||||
long accountCreationTime = getTodayMinusNDays(SIGN_AGE_1 + 1);
|
||||
|
||||
KeyPair peerKeyPair = Sig.generateKeyPair();
|
||||
KeyPair signerKeyPair = Sig.generateKeyPair();
|
||||
|
||||
when(keyRing.getSignatureKeyPair()).thenReturn(signerKeyPair);
|
||||
|
||||
|
||||
AccountAgeWitness accountAgeWitness = new AccountAgeWitness(account1DataHash, accountCreationTime);
|
||||
signedWitnessService.signAndPublishAccountAgeWitness(SignedWitnessService.MINIMUM_TRADE_AMOUNT_FOR_SIGNING, accountAgeWitness, peerKeyPair.getPublic());
|
||||
|
||||
verify(p2pService, times(1)).addPersistableNetworkPayload(any(PersistableNetworkPayload.class), anyBoolean());
|
||||
}
|
||||
|
||||
/* Signed witness tree
|
||||
Each edge in the graph represents one signature
|
||||
|
||||
Arbitrator
|
||||
|
|
||||
sw1
|
||||
|
|
||||
sw2
|
||||
|
|
||||
sw3
|
||||
*/
|
||||
@Test
|
||||
public void testBanFilterSingleTree() {
|
||||
SignedWitness sw1 = new SignedWitness(ARBITRATOR, account1DataHash, signature1, signer1PubKey, witnessOwner1PubKey, date1, tradeAmount1);
|
||||
SignedWitness sw2 = new SignedWitness(TRADE, account2DataHash, signature2, signer2PubKey, witnessOwner2PubKey, date2, tradeAmount2);
|
||||
SignedWitness sw3 = new SignedWitness(TRADE, account3DataHash, signature3, signer3PubKey, witnessOwner3PubKey, date3, tradeAmount3);
|
||||
|
||||
signedWitnessService.addToMap(sw1);
|
||||
signedWitnessService.addToMap(sw2);
|
||||
signedWitnessService.addToMap(sw3);
|
||||
|
||||
// Second account is banned, first account is still a signer but the other two are no longer signers
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(true);
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
|
||||
// First account is banned, no accounts in the tree below it are signers
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(true);
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(false);
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
}
|
||||
|
||||
/* Signed witness trees
|
||||
Each edge in the graph represents one signature
|
||||
|
||||
Arbitrator
|
||||
| |
|
||||
sw1 sw2
|
||||
|
|
||||
sw3
|
||||
*/
|
||||
@Test
|
||||
public void testBanFilterTwoTrees() {
|
||||
// Signer 2 is signed by arbitrator
|
||||
signer2PubKey = arbitrator1Key.getPubKey();
|
||||
signature2 = arbitrator1Key.signMessage(Utilities.encodeToHex(account2DataHash)).getBytes(Charsets.UTF_8);
|
||||
|
||||
SignedWitness sw1 = new SignedWitness(ARBITRATOR, account1DataHash, signature1, signer1PubKey, witnessOwner1PubKey, date1, tradeAmount1);
|
||||
SignedWitness sw2 = new SignedWitness(ARBITRATOR, account2DataHash, signature2, signer2PubKey, witnessOwner2PubKey, date2, tradeAmount2);
|
||||
SignedWitness sw3 = new SignedWitness(TRADE, account3DataHash, signature3, signer3PubKey, witnessOwner3PubKey, date3, tradeAmount3);
|
||||
|
||||
signedWitnessService.addToMap(sw1);
|
||||
signedWitnessService.addToMap(sw2);
|
||||
signedWitnessService.addToMap(sw3);
|
||||
|
||||
// Only second account is banned, first account is still a signer but the other two are no longer signers
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(true);
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
|
||||
// Only first account is banned, account2 and account3 are still signers
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(true);
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(false);
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
}
|
||||
|
||||
/* Signed witness tree
|
||||
Each edge in the graph represents one signature
|
||||
|
||||
Arbitrator
|
||||
| |
|
||||
sw1 sw2
|
||||
\ /
|
||||
sw3
|
||||
*/
|
||||
@Test
|
||||
public void testBanFilterJoinedTrees() throws Exception {
|
||||
// Signer 2 is signed by arbitrator
|
||||
signer2PubKey = arbitrator1Key.getPubKey();
|
||||
signature2 = arbitrator1Key.signMessage(Utilities.encodeToHex(account2DataHash)).getBytes(Charsets.UTF_8);
|
||||
|
||||
// Peer1 owns both account1 and account2
|
||||
// witnessOwner2PubKey = witnessOwner1PubKey;
|
||||
// peer2KeyPair = peer1KeyPair;
|
||||
// signature3 = Sig.sign(peer2KeyPair.getPrivate(), Utilities.encodeToHex(account3DataHash).getBytes(Charsets.UTF_8));
|
||||
|
||||
// sw1 also signs sw3 (not supported yet but a possible addition for a more robust system)
|
||||
var signature3p = Sig.sign(peer1KeyPair.getPrivate(), Utilities.encodeToHex(account3DataHash).getBytes(Charsets.UTF_8));
|
||||
var signer3pPubKey = witnessOwner1PubKey;
|
||||
var date3p = date3;
|
||||
var tradeAmount3p = tradeAmount3;
|
||||
|
||||
SignedWitness sw1 = new SignedWitness(ARBITRATOR, account1DataHash, signature1, signer1PubKey, witnessOwner1PubKey, date1, tradeAmount1);
|
||||
SignedWitness sw2 = new SignedWitness(ARBITRATOR, account2DataHash, signature2, signer2PubKey, witnessOwner2PubKey, date2, tradeAmount2);
|
||||
SignedWitness sw3 = new SignedWitness(TRADE, account3DataHash, signature3, signer3PubKey, witnessOwner3PubKey, date3, tradeAmount3);
|
||||
SignedWitness sw3p = new SignedWitness(TRADE, account3DataHash, signature3p, signer3pPubKey, witnessOwner3PubKey, date3p, tradeAmount3p);
|
||||
|
||||
signedWitnessService.addToMap(sw1);
|
||||
signedWitnessService.addToMap(sw2);
|
||||
signedWitnessService.addToMap(sw3);
|
||||
signedWitnessService.addToMap(sw3p);
|
||||
|
||||
// First account is banned, the other two are still signers
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(true);
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
|
||||
// Second account is banned, the other two are still signers
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(false);
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(true);
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
|
||||
// First and second account is banned, the third is no longer a signer
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(true);
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(true);
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew1));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew2));
|
||||
assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package bisq.core.account.sign;
|
||||
|
||||
import bisq.common.crypto.Sig;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.ECKey;
|
||||
import org.bitcoinj.core.Utils;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static bisq.core.account.sign.SignedWitness.VerificationMethod.ARBITRATOR;
|
||||
import static bisq.core.account.sign.SignedWitness.VerificationMethod.TRADE;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class SignedWitnessTest {
|
||||
|
||||
private ECKey arbitrator1Key;
|
||||
private byte[] witnessOwner1PubKey;
|
||||
private byte[] witnessHash;
|
||||
private byte[] witnessHashSignature;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
arbitrator1Key = new ECKey();
|
||||
witnessOwner1PubKey = Sig.getPublicKeyBytes(Sig.generateKeyPair().getPublic());
|
||||
witnessHash = Utils.sha256hash160(new byte[]{1});
|
||||
witnessHashSignature = arbitrator1Key.signMessage(Utilities.encodeToHex(witnessHash)).getBytes(Charsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProtoRoundTrip() {
|
||||
SignedWitness signedWitness = new SignedWitness(ARBITRATOR, witnessHash, witnessHashSignature, arbitrator1Key.getPubKey(), witnessOwner1PubKey, Instant.now().getEpochSecond(), 100);
|
||||
assertEquals(signedWitness, SignedWitness.fromProto(signedWitness.toProtoMessage().getSignedWitness()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isImmutable() {
|
||||
byte[] signerPubkey = arbitrator1Key.getPubKey();
|
||||
SignedWitness signedWitness = new SignedWitness(TRADE, witnessHash, witnessHashSignature, signerPubkey, witnessOwner1PubKey, Instant.now().getEpochSecond(), 100);
|
||||
byte[] originalWitnessHash = signedWitness.getAccountAgeWitnessHash().clone();
|
||||
witnessHash[0] += 1;
|
||||
assertArrayEquals(originalWitnessHash, signedWitness.getAccountAgeWitnessHash());
|
||||
|
||||
byte[] originalWitnessHashSignature = signedWitness.getSignature().clone();
|
||||
witnessHashSignature[0] += 1;
|
||||
assertArrayEquals(originalWitnessHashSignature, signedWitness.getSignature());
|
||||
|
||||
byte[] originalSignerPubkey = signedWitness.getSignerPubKey().clone();
|
||||
signerPubkey[0] += 1;
|
||||
assertArrayEquals(originalSignerPubkey, signedWitness.getSignerPubKey());
|
||||
byte[] originalwitnessOwner1PubKey = signedWitness.getWitnessOwnerPubKey().clone();
|
||||
witnessOwner1PubKey[0] += 1;
|
||||
assertArrayEquals(originalwitnessOwner1PubKey, signedWitness.getWitnessOwnerPubKey());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* 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.account.witness;
|
||||
|
||||
import bisq.core.account.sign.SignedWitness;
|
||||
import bisq.core.account.sign.SignedWitnessService;
|
||||
import bisq.core.filter.FilterManager;
|
||||
import bisq.core.locale.CountryUtil;
|
||||
import bisq.core.offer.OfferPayload;
|
||||
import bisq.core.payment.ChargeBackRisk;
|
||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
||||
import bisq.core.payment.payload.PaymentMethod;
|
||||
import bisq.core.payment.payload.SepaAccountPayload;
|
||||
import bisq.core.support.SupportType;
|
||||
import bisq.core.support.dispute.Dispute;
|
||||
import bisq.core.support.dispute.DisputeResult;
|
||||
import bisq.core.support.dispute.arbitration.TraderDataItem;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||
import bisq.core.trade.Contract;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService;
|
||||
|
||||
import bisq.common.crypto.CryptoException;
|
||||
import bisq.common.crypto.Hash;
|
||||
import bisq.common.crypto.KeyRing;
|
||||
import bisq.common.crypto.KeyStorage;
|
||||
import bisq.common.crypto.PubKeyRing;
|
||||
import bisq.common.crypto.Sig;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.ECKey;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static bisq.core.payment.payload.PaymentMethod.getPaymentMethodById;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
// Restricted default Java security policy on Travis does not allow long keys, so test fails.
|
||||
// Using Utilities.removeCryptographyRestrictions(); did not work.
|
||||
//@Ignore
|
||||
public class AccountAgeWitnessServiceTest {
|
||||
private PublicKey publicKey;
|
||||
private KeyPair keypair;
|
||||
private SignedWitnessService signedWitnessService;
|
||||
private AccountAgeWitnessService service;
|
||||
private ChargeBackRisk chargeBackRisk;
|
||||
private FilterManager filterManager;
|
||||
private File dir1;
|
||||
private File dir2;
|
||||
private File dir3;
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
KeyRing keyRing = mock(KeyRing.class);
|
||||
setupService(keyRing);
|
||||
keypair = Sig.generateKeyPair();
|
||||
publicKey = keypair.getPublic();
|
||||
// Setup temp storage dir
|
||||
dir1 = makeDir("temp_tests1");
|
||||
dir2 = makeDir("temp_tests1");
|
||||
dir3 = makeDir("temp_tests1");
|
||||
}
|
||||
|
||||
private void setupService(KeyRing keyRing) {
|
||||
chargeBackRisk = mock(ChargeBackRisk.class);
|
||||
AppendOnlyDataStoreService dataStoreService = mock(AppendOnlyDataStoreService.class);
|
||||
P2PService p2pService = mock(P2PService.class);
|
||||
ArbitratorManager arbitratorManager = mock(ArbitratorManager.class);
|
||||
when(arbitratorManager.isPublicKeyInList(any())).thenReturn(true);
|
||||
AppendOnlyDataStoreService appendOnlyDataStoreService = mock(AppendOnlyDataStoreService.class);
|
||||
filterManager = mock(FilterManager.class);
|
||||
signedWitnessService = new SignedWitnessService(keyRing, p2pService, arbitratorManager, null, appendOnlyDataStoreService, null, filterManager);
|
||||
service = new AccountAgeWitnessService(null, null, null, signedWitnessService, chargeBackRisk, null, dataStoreService, null, filterManager);
|
||||
}
|
||||
|
||||
private File makeDir(String name) throws IOException {
|
||||
var dir = File.createTempFile(name, "");
|
||||
dir.delete();
|
||||
dir.mkdir();
|
||||
return dir;
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
// Do teardown stuff
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testIsTradeDateAfterReleaseDate() {
|
||||
Date ageWitnessReleaseDate = new GregorianCalendar(2017, Calendar.OCTOBER, 23).getTime();
|
||||
Date tradeDate = new GregorianCalendar(2017, Calendar.NOVEMBER, 1).getTime();
|
||||
assertTrue(service.isDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate, errorMessage -> {
|
||||
}));
|
||||
tradeDate = new GregorianCalendar(2017, Calendar.OCTOBER, 23).getTime();
|
||||
assertTrue(service.isDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate, errorMessage -> {
|
||||
}));
|
||||
tradeDate = new GregorianCalendar(2017, Calendar.OCTOBER, 22, 0, 0, 1).getTime();
|
||||
assertTrue(service.isDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate, errorMessage -> {
|
||||
}));
|
||||
tradeDate = new GregorianCalendar(2017, Calendar.OCTOBER, 22).getTime();
|
||||
assertFalse(service.isDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate, errorMessage -> {
|
||||
}));
|
||||
tradeDate = new GregorianCalendar(2017, Calendar.OCTOBER, 21).getTime();
|
||||
assertFalse(service.isDateAfterReleaseDate(tradeDate.getTime(), ageWitnessReleaseDate, errorMessage -> {
|
||||
}));
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void testVerifySignatureOfNonce() throws CryptoException {
|
||||
byte[] nonce = new byte[]{0x01};
|
||||
byte[] signature = Sig.sign(keypair.getPrivate(), nonce);
|
||||
assertTrue(service.verifySignature(publicKey, nonce, signature, errorMessage -> {
|
||||
}));
|
||||
assertFalse(service.verifySignature(publicKey, nonce, new byte[]{0x02}, errorMessage -> {
|
||||
}));
|
||||
assertFalse(service.verifySignature(publicKey, new byte[]{0x03}, signature, errorMessage -> {
|
||||
}));
|
||||
assertFalse(service.verifySignature(publicKey, new byte[]{0x02}, new byte[]{0x04}, errorMessage -> {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArbitratorSignWitness() {
|
||||
KeyRing buyerKeyRing = new KeyRing(new KeyStorage(dir1));
|
||||
KeyRing sellerKeyRing = new KeyRing(new KeyStorage(dir2));
|
||||
|
||||
// Setup dispute for arbitrator to sign both sides
|
||||
List<Dispute> disputes = new ArrayList<>();
|
||||
PubKeyRing buyerPubKeyRing = buyerKeyRing.getPubKeyRing();
|
||||
PubKeyRing sellerPubKeyRing = sellerKeyRing.getPubKeyRing();
|
||||
PaymentAccountPayload buyerPaymentAccountPayload = new SepaAccountPayload(PaymentMethod.SEPA_ID, "1", CountryUtil.getAllSepaCountries());
|
||||
PaymentAccountPayload sellerPaymentAccountPayload = new SepaAccountPayload(PaymentMethod.SEPA_ID, "2", CountryUtil.getAllSepaCountries());
|
||||
AccountAgeWitness buyerAccountAgeWitness = service.getNewWitness(buyerPaymentAccountPayload, buyerPubKeyRing);
|
||||
service.addToMap(buyerAccountAgeWitness);
|
||||
AccountAgeWitness sellerAccountAgeWitness = service.getNewWitness(sellerPaymentAccountPayload, sellerPubKeyRing);
|
||||
service.addToMap(sellerAccountAgeWitness);
|
||||
long now = new Date().getTime() + 1000;
|
||||
Contract contract = mock(Contract.class);
|
||||
disputes.add(new Dispute(new Date().getTime(),
|
||||
"trade1",
|
||||
0,
|
||||
true,
|
||||
true,
|
||||
buyerPubKeyRing,
|
||||
now - 1,
|
||||
now - 1,
|
||||
contract,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"contractAsJson",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
SupportType.ARBITRATION));
|
||||
disputes.get(0).setIsClosed();
|
||||
disputes.get(0).getDisputeResultProperty().set(new DisputeResult(
|
||||
"trade1",
|
||||
1,
|
||||
DisputeResult.Winner.BUYER,
|
||||
DisputeResult.Reason.OTHER.ordinal(),
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
"summary",
|
||||
null,
|
||||
null,
|
||||
100000,
|
||||
0,
|
||||
null,
|
||||
now - 1,
|
||||
false));
|
||||
|
||||
// Filtermanager says nothing is filtered
|
||||
when(filterManager.isNodeAddressBanned(any())).thenReturn(false);
|
||||
when(filterManager.isCurrencyBanned(any())).thenReturn(false);
|
||||
when(filterManager.isPaymentMethodBanned(any())).thenReturn(false);
|
||||
when(filterManager.arePeersPaymentAccountDataBanned(any())).thenReturn(false);
|
||||
when(filterManager.isWitnessSignerPubKeyBanned(any())).thenReturn(false);
|
||||
|
||||
when(chargeBackRisk.hasChargebackRisk(any(), any())).thenReturn(true);
|
||||
|
||||
when(contract.getPaymentMethodId()).thenReturn(PaymentMethod.SEPA_ID);
|
||||
when(contract.getTradeAmount()).thenReturn(Coin.parseCoin("0.01"));
|
||||
when(contract.getBuyerPubKeyRing()).thenReturn(buyerPubKeyRing);
|
||||
when(contract.getSellerPubKeyRing()).thenReturn(sellerPubKeyRing);
|
||||
when(contract.getBuyerPaymentAccountPayload()).thenReturn(buyerPaymentAccountPayload);
|
||||
when(contract.getSellerPaymentAccountPayload()).thenReturn(sellerPaymentAccountPayload);
|
||||
when(contract.getOfferPayload()).thenReturn(mock(OfferPayload.class));
|
||||
List<TraderDataItem> items = service.getTraderPaymentAccounts(now, getPaymentMethodById(PaymentMethod.SEPA_ID), disputes);
|
||||
assertEquals(2, items.size());
|
||||
|
||||
// Setup a mocked arbitrator key
|
||||
ECKey arbitratorKey = mock(ECKey.class);
|
||||
when(arbitratorKey.signMessage(any())).thenReturn("1");
|
||||
when(arbitratorKey.signMessage(any())).thenReturn("2");
|
||||
when(arbitratorKey.getPubKey()).thenReturn("1".getBytes());
|
||||
|
||||
// Arbitrator signs both trader accounts
|
||||
items.forEach(item -> service.arbitratorSignAccountAgeWitness(
|
||||
item.getTradeAmount(),
|
||||
item.getAccountAgeWitness(),
|
||||
arbitratorKey,
|
||||
item.getPeersPubKey()));
|
||||
|
||||
// Check that both accountAgeWitnesses are signed
|
||||
SignedWitness foundBuyerSignedWitness = signedWitnessService.getSignedWitnessSetByOwnerPubKey(
|
||||
buyerPubKeyRing.getSignaturePubKeyBytes()).stream()
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
assert foundBuyerSignedWitness != null;
|
||||
assertEquals(Utilities.bytesAsHexString(foundBuyerSignedWitness.getAccountAgeWitnessHash()),
|
||||
Utilities.bytesAsHexString(buyerAccountAgeWitness.getHash()));
|
||||
SignedWitness foundSellerSignedWitness = signedWitnessService.getSignedWitnessSetByOwnerPubKey(
|
||||
sellerPubKeyRing.getSignaturePubKeyBytes()).stream()
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
assert foundSellerSignedWitness != null;
|
||||
assertEquals(Utilities.bytesAsHexString(foundSellerSignedWitness.getAccountAgeWitnessHash()),
|
||||
Utilities.bytesAsHexString(sellerAccountAgeWitness.getHash()));
|
||||
}
|
||||
|
||||
// Create a tree of signed witnesses Arb -(SWA)-> aew1 -(SW1)-> aew2 -(SW2)-> aew3
|
||||
// Delete SWA signature, none of the account age witnesses are considered signed
|
||||
// Sign a dummy AccountAgeWitness using the signerPubkey from SW1; aew2 and aew3 are not considered signed. The
|
||||
// lost SignedWitness isn't possible to recover so aew1 is still not signed, but it's pubkey is a signer.
|
||||
@Test
|
||||
public void testArbitratorSignDummyWitness() throws CryptoException {
|
||||
ECKey arbitratorKey = new ECKey();
|
||||
// Init 2 user accounts
|
||||
var user1KeyRing = new KeyRing(new KeyStorage(dir1));
|
||||
var user2KeyRing = new KeyRing(new KeyStorage(dir2));
|
||||
var user3KeyRing = new KeyRing(new KeyStorage(dir3));
|
||||
var pubKeyRing1 = user1KeyRing.getPubKeyRing();
|
||||
var pubKeyRing2 = user2KeyRing.getPubKeyRing();
|
||||
var pubKeyRing3 = user3KeyRing.getPubKeyRing();
|
||||
var account1 = new SepaAccountPayload(PaymentMethod.SEPA_ID, "1", CountryUtil.getAllSepaCountries());
|
||||
var account2 = new SepaAccountPayload(PaymentMethod.SEPA_ID, "2", CountryUtil.getAllSepaCountries());
|
||||
var account3 = new SepaAccountPayload(PaymentMethod.SEPA_ID, "3", CountryUtil.getAllSepaCountries());
|
||||
var aew1 = service.getNewWitness(account1, pubKeyRing1);
|
||||
var aew2 = service.getNewWitness(account2, pubKeyRing2);
|
||||
var aew3 = service.getNewWitness(account3, pubKeyRing3);
|
||||
// Backdate witness1 70 days
|
||||
aew1 = new AccountAgeWitness(aew1.getHash(), new Date().getTime() - TimeUnit.DAYS.toMillis(70));
|
||||
aew2 = new AccountAgeWitness(aew2.getHash(), new Date().getTime() - TimeUnit.DAYS.toMillis(35));
|
||||
aew3 = new AccountAgeWitness(aew3.getHash(), new Date().getTime() - TimeUnit.DAYS.toMillis(1));
|
||||
service.addToMap(aew1);
|
||||
service.addToMap(aew2);
|
||||
service.addToMap(aew3);
|
||||
|
||||
// Test as user1. It's still possible to sign as arbitrator since the ECKey is passed as an argument.
|
||||
setupService(user1KeyRing);
|
||||
|
||||
// Arbitrator signs user1
|
||||
service.arbitratorSignAccountAgeWitness(aew1, arbitratorKey, pubKeyRing1.getSignaturePubKeyBytes(),
|
||||
aew1.getDate());
|
||||
// user1 signs user2
|
||||
signAccountAgeWitness(aew2, pubKeyRing2.getSignaturePubKey(), aew2.getDate(), user1KeyRing);
|
||||
// user2 signs user3
|
||||
signAccountAgeWitness(aew3, pubKeyRing3.getSignaturePubKey(), aew3.getDate(), user2KeyRing);
|
||||
signedWitnessService.signAndPublishAccountAgeWitness(SignedWitnessService.MINIMUM_TRADE_AMOUNT_FOR_SIGNING, aew2,
|
||||
pubKeyRing2.getSignaturePubKey());
|
||||
assertTrue(service.accountIsSigner(aew1));
|
||||
assertTrue(service.accountIsSigner(aew2));
|
||||
assertFalse(service.accountIsSigner(aew3));
|
||||
assertTrue(signedWitnessService.isSignedAccountAgeWitness(aew3));
|
||||
|
||||
// Remove SignedWitness signed by arbitrator
|
||||
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
||||
var signedWitnessArb = signedWitnessService.getSignedWitnessMapValues().stream()
|
||||
.filter(sw -> sw.getVerificationMethod() == SignedWitness.VerificationMethod.ARBITRATOR)
|
||||
.findAny()
|
||||
.get();
|
||||
signedWitnessService.removeSignedWitness(signedWitnessArb);
|
||||
assertEquals(signedWitnessService.getSignedWitnessMapValues().size(), 2);
|
||||
|
||||
// Check that no account age witness is a signer
|
||||
assertFalse(service.accountIsSigner(aew1));
|
||||
assertFalse(service.accountIsSigner(aew2));
|
||||
assertFalse(service.accountIsSigner(aew3));
|
||||
assertFalse(signedWitnessService.isSignedAccountAgeWitness(aew2));
|
||||
|
||||
// Sign dummy AccountAgeWitness using signer key from SW_1
|
||||
assertEquals(signedWitnessService.getRootSignedWitnessSet(false).size(), 1);
|
||||
|
||||
// TODO: move this to accountagewitnessservice
|
||||
@SuppressWarnings("OptionalGetWithoutIsPresent")
|
||||
var orphanedSignedWitness = signedWitnessService.getRootSignedWitnessSet(false).stream().findAny().get();
|
||||
var dummyAccountAgeWitnessHash = Hash.getRipemd160hash(orphanedSignedWitness.getSignerPubKey());
|
||||
var dummyAEW = new AccountAgeWitness(dummyAccountAgeWitnessHash,
|
||||
orphanedSignedWitness.getDate() -
|
||||
(TimeUnit.DAYS.toMillis(SignedWitnessService.SIGNER_AGE_DAYS + 1)));
|
||||
service.arbitratorSignAccountAgeWitness(
|
||||
dummyAEW, arbitratorKey, orphanedSignedWitness.getSignerPubKey(), dummyAEW.getDate());
|
||||
|
||||
assertFalse(service.accountIsSigner(aew1));
|
||||
assertTrue(service.accountIsSigner(aew2));
|
||||
assertFalse(service.accountIsSigner(aew3));
|
||||
assertTrue(signedWitnessService.isSignedAccountAgeWitness(aew2));
|
||||
}
|
||||
|
||||
private void signAccountAgeWitness(AccountAgeWitness accountAgeWitness,
|
||||
PublicKey witnessOwnerPubKey,
|
||||
long time,
|
||||
KeyRing signerKeyRing) throws CryptoException {
|
||||
byte[] signature = Sig.sign(signerKeyRing.getSignatureKeyPair().getPrivate(), accountAgeWitness.getHash());
|
||||
SignedWitness signedWitness = new SignedWitness(SignedWitness.VerificationMethod.TRADE,
|
||||
accountAgeWitness.getHash(),
|
||||
signature,
|
||||
signerKeyRing.getSignatureKeyPair().getPublic().getEncoded(),
|
||||
witnessOwnerPubKey.getEncoded(),
|
||||
time,
|
||||
SignedWitnessService.MINIMUM_TRADE_AMOUNT_FOR_SIGNING.value);
|
||||
signedWitnessService.addToMap(signedWitness);
|
||||
}
|
||||
|
||||
}
|
142
core/src/test/java/bisq/core/app/BisqHelpFormatterTest.java
Normal file
142
core/src/test/java/bisq/core/app/BisqHelpFormatterTest.java
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* 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.app;
|
||||
|
||||
import bisq.common.config.BisqHelpFormatter;
|
||||
|
||||
import joptsimple.OptionParser;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class BisqHelpFormatterTest {
|
||||
|
||||
@Test
|
||||
public void testHelpFormatter() throws IOException, URISyntaxException {
|
||||
|
||||
OptionParser parser = new OptionParser();
|
||||
|
||||
parser.formatHelpWith(new BisqHelpFormatter("Bisq Test", "bisq-test", "0.1.0"));
|
||||
|
||||
parser.accepts("name",
|
||||
"The name of the Bisq node")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class)
|
||||
.defaultsTo("Bisq");
|
||||
|
||||
parser.accepts("another-option",
|
||||
"This is a long description which will need to break over multiple linessssssssssss such " +
|
||||
"that no line is longer than 80 characters in the help output.")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class)
|
||||
.defaultsTo("WAT");
|
||||
|
||||
parser.accepts("exactly-72-char-description",
|
||||
"012345678911234567892123456789312345678941234567895123456789612345678971")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class);
|
||||
|
||||
parser.accepts("exactly-72-char-description-with-spaces",
|
||||
" 123456789 123456789 123456789 123456789 123456789 123456789 123456789 1")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class);
|
||||
|
||||
parser.accepts("90-char-description-without-spaces",
|
||||
"-123456789-223456789-323456789-423456789-523456789-623456789-723456789-823456789-923456789")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class);
|
||||
|
||||
parser.accepts("90-char-description-with-space-at-char-80",
|
||||
"-123456789-223456789-323456789-423456789-523456789-623456789-723456789-823456789 923456789")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class);
|
||||
|
||||
parser.accepts("90-char-description-with-spaces-at-chars-5-and-80",
|
||||
"-123 56789-223456789-323456789-423456789-523456789-623456789-723456789-823456789 923456789")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class);
|
||||
|
||||
parser.accepts("90-char-description-with-space-at-char-73",
|
||||
"-123456789-223456789-323456789-423456789-523456789-623456789-723456789-8 3456789-923456789")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class);
|
||||
|
||||
parser.accepts("1-char-description-with-only-a-space", " ")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class);
|
||||
|
||||
parser.accepts("empty-description", "")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class);
|
||||
|
||||
parser.accepts("no-description")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class);
|
||||
|
||||
parser.accepts("no-arg", "Some description");
|
||||
|
||||
parser.accepts("optional-arg",
|
||||
"Option description")
|
||||
.withOptionalArg();
|
||||
|
||||
parser.accepts("with-default-value",
|
||||
"Some option with a default value")
|
||||
.withRequiredArg()
|
||||
.ofType(String.class)
|
||||
.defaultsTo("Wat");
|
||||
|
||||
parser.accepts("data-dir",
|
||||
"Application data directory")
|
||||
.withRequiredArg()
|
||||
.ofType(File.class)
|
||||
.defaultsTo(new File("/Users/cbeams/Library/Application Support/Bisq"));
|
||||
|
||||
parser.accepts("enum-opt",
|
||||
"Some option that accepts an enum value as an argument")
|
||||
.withRequiredArg()
|
||||
.ofType(AnEnum.class)
|
||||
.defaultsTo(AnEnum.foo);
|
||||
|
||||
ByteArrayOutputStream actual = new ByteArrayOutputStream();
|
||||
String expected = new String(Files.readAllBytes(Paths.get(getClass().getResource("cli-output.txt").toURI())));
|
||||
if (System.getProperty("os.name").startsWith("Windows")) {
|
||||
// Load the expected content from a different file for Windows due to different path separator
|
||||
// And normalize line endings to LF in case the file has CRLF line endings
|
||||
expected = new String(Files.readAllBytes(Paths.get(getClass().getResource("cli-output_windows.txt").toURI())))
|
||||
.replaceAll("\\r\\n?", "\n");
|
||||
}
|
||||
|
||||
parser.printHelpOn(new PrintStream(actual));
|
||||
assertThat(actual.toString(), equalTo(expected));
|
||||
}
|
||||
|
||||
|
||||
enum AnEnum {foo, bar, baz}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* 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.arbitration;
|
||||
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorService;
|
||||
import bisq.core.user.User;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class ArbitratorManagerTest {
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testIsArbitratorAvailableForLanguage() {
|
||||
User user = mock(User.class);
|
||||
ArbitratorService arbitratorService = mock(ArbitratorService.class);
|
||||
|
||||
ArbitratorManager manager = new ArbitratorManager(null, arbitratorService, user, null, false);
|
||||
|
||||
ArrayList<String> languagesOne = new ArrayList<String>() {{
|
||||
add("en");
|
||||
add("de");
|
||||
}};
|
||||
|
||||
ArrayList<String> languagesTwo = new ArrayList<String>() {{
|
||||
add("en");
|
||||
add("es");
|
||||
}};
|
||||
|
||||
Arbitrator one = new Arbitrator(new NodeAddress("arbitrator:1"), null, null, null,
|
||||
languagesOne, 0L, null, "", null,
|
||||
null, null);
|
||||
|
||||
Arbitrator two = new Arbitrator(new NodeAddress("arbitrator:2"), null, null, null,
|
||||
languagesTwo, 0L, null, "", null,
|
||||
null, null);
|
||||
|
||||
manager.addDisputeAgent(one, () -> {
|
||||
}, errorMessage -> {
|
||||
});
|
||||
manager.addDisputeAgent(two, () -> {
|
||||
}, errorMessage -> {
|
||||
});
|
||||
|
||||
assertTrue(manager.isAgentAvailableForLanguage("en"));
|
||||
assertFalse(manager.isAgentAvailableForLanguage("th"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetArbitratorLanguages() {
|
||||
User user = mock(User.class);
|
||||
ArbitratorService arbitratorService = mock(ArbitratorService.class);
|
||||
|
||||
ArbitratorManager manager = new ArbitratorManager(null, arbitratorService, user, null, false);
|
||||
|
||||
ArrayList<String> languagesOne = new ArrayList<String>() {{
|
||||
add("en");
|
||||
add("de");
|
||||
}};
|
||||
|
||||
ArrayList<String> languagesTwo = new ArrayList<String>() {{
|
||||
add("en");
|
||||
add("es");
|
||||
}};
|
||||
|
||||
Arbitrator one = new Arbitrator(new NodeAddress("arbitrator:1"), null, null, null,
|
||||
languagesOne, 0L, null, "", null,
|
||||
null, null);
|
||||
|
||||
Arbitrator two = new Arbitrator(new NodeAddress("arbitrator:2"), null, null, null,
|
||||
languagesTwo, 0L, null, "", null,
|
||||
null, null);
|
||||
|
||||
ArrayList<NodeAddress> nodeAddresses = new ArrayList<NodeAddress>() {{
|
||||
add(two.getNodeAddress());
|
||||
}};
|
||||
|
||||
manager.addDisputeAgent(one, () -> {
|
||||
}, errorMessage -> {
|
||||
});
|
||||
manager.addDisputeAgent(two, () -> {
|
||||
}, errorMessage -> {
|
||||
});
|
||||
|
||||
assertThat(manager.getDisputeAgentLanguages(nodeAddresses), containsInAnyOrder("en", "es"));
|
||||
assertThat(manager.getDisputeAgentLanguages(nodeAddresses), not(containsInAnyOrder("de")));
|
||||
}
|
||||
|
||||
}
|
60
core/src/test/java/bisq/core/arbitration/ArbitratorTest.java
Normal file
60
core/src/test/java/bisq/core/arbitration/ArbitratorTest.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.arbitration;
|
||||
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
|
||||
import bisq.common.crypto.PubKeyRing;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Ignore;
|
||||
|
||||
@SuppressWarnings({"SameParameterValue", "UnusedAssignment"})
|
||||
public class ArbitratorTest {
|
||||
|
||||
@Ignore("TODO InvalidKeySpecException at bisq.common.crypto.Sig.getPublicKeyFromBytes(Sig.java:135)")
|
||||
public void testRoundtrip() {
|
||||
Arbitrator arbitrator = getArbitratorMock();
|
||||
|
||||
|
||||
Arbitrator newVo = Arbitrator.fromProto(arbitrator.toProtoMessage().getArbitrator());
|
||||
}
|
||||
|
||||
public static Arbitrator getArbitratorMock() {
|
||||
return new Arbitrator(new NodeAddress("host", 1000),
|
||||
getBytes(100),
|
||||
"btcaddress",
|
||||
new PubKeyRing(getBytes(100), getBytes(100)),
|
||||
Lists.newArrayList(),
|
||||
new Date().getTime(),
|
||||
getBytes(100),
|
||||
"registrationSignature",
|
||||
null, null, null);
|
||||
}
|
||||
|
||||
public static byte[] getBytes(@SuppressWarnings("SameParameterValue") int count) {
|
||||
return RandomUtils.nextBytes(count);
|
||||
}
|
||||
}
|
56
core/src/test/java/bisq/core/arbitration/MediatorTest.java
Normal file
56
core/src/test/java/bisq/core/arbitration/MediatorTest.java
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.arbitration;
|
||||
|
||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
|
||||
import bisq.common.crypto.PubKeyRing;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Ignore;
|
||||
|
||||
import static bisq.core.arbitration.ArbitratorTest.getBytes;
|
||||
|
||||
public class MediatorTest {
|
||||
|
||||
@Ignore("TODO InvalidKeySpecException at bisq.common.crypto.Sig.getPublicKeyFromBytes(Sig.java:135)")
|
||||
public void testRoundtrip() {
|
||||
Mediator Mediator = getMediatorMock();
|
||||
|
||||
|
||||
//noinspection AccessStaticViaInstance
|
||||
Mediator.fromProto(Mediator.toProtoMessage().getMediator());
|
||||
}
|
||||
|
||||
public static Mediator getMediatorMock() {
|
||||
return new Mediator(new NodeAddress("host", 1000),
|
||||
new PubKeyRing(getBytes(100), getBytes(100)),
|
||||
Lists.newArrayList(),
|
||||
new Date().getTime(),
|
||||
getBytes(100),
|
||||
"registrationSignature",
|
||||
"email",
|
||||
"info",
|
||||
null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package bisq.core.arbitration;
|
||||
|
||||
import bisq.core.account.witness.AccountAgeWitness;
|
||||
import bisq.core.support.dispute.arbitration.TraderDataItem;
|
||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
public class TraderDataItemTest {
|
||||
private TraderDataItem traderDataItem1;
|
||||
private TraderDataItem traderDataItem2;
|
||||
private TraderDataItem traderDataItem3;
|
||||
private AccountAgeWitness accountAgeWitness1;
|
||||
private AccountAgeWitness accountAgeWitness2;
|
||||
private byte[] hash1 = "1".getBytes();
|
||||
private byte[] hash2 = "2".getBytes();
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
accountAgeWitness1 = new AccountAgeWitness(hash1, 123);
|
||||
accountAgeWitness2 = new AccountAgeWitness(hash2, 124);
|
||||
traderDataItem1 = new TraderDataItem(mock(PaymentAccountPayload.class), accountAgeWitness1, Coin.valueOf(546),
|
||||
mock(PublicKey.class));
|
||||
traderDataItem2 = new TraderDataItem(mock(PaymentAccountPayload.class), accountAgeWitness1, Coin.valueOf(547),
|
||||
mock(PublicKey.class));
|
||||
traderDataItem3 = new TraderDataItem(mock(PaymentAccountPayload.class), accountAgeWitness2, Coin.valueOf(548),
|
||||
mock(PublicKey.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
assertEquals(traderDataItem1, traderDataItem2);
|
||||
assertNotEquals(traderDataItem1, traderDataItem3);
|
||||
assertNotEquals(traderDataItem2, traderDataItem3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashCode() {
|
||||
assertEquals(traderDataItem1.hashCode(), traderDataItem2.hashCode());
|
||||
assertNotEquals(traderDataItem1.hashCode(), traderDataItem3.hashCode());
|
||||
}
|
||||
}
|
141
core/src/test/java/bisq/core/btc/TxFeeEstimationServiceTest.java
Normal file
141
core/src/test/java/bisq/core/btc/TxFeeEstimationServiceTest.java
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* 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.btc;
|
||||
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.InsufficientMoneyException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class TxFeeEstimationServiceTest {
|
||||
|
||||
@Test
|
||||
public void testGetEstimatedTxVsize_withDefaultTxVsize() throws InsufficientMoneyException {
|
||||
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
|
||||
int initialEstimatedTxVsize;
|
||||
Coin txFeePerVbyte;
|
||||
BtcWalletService btcWalletService = mock(BtcWalletService.class);
|
||||
int result;
|
||||
int realTxVsize;
|
||||
Coin txFee;
|
||||
|
||||
initialEstimatedTxVsize = 175;
|
||||
txFeePerVbyte = Coin.valueOf(10);
|
||||
realTxVsize = 175;
|
||||
|
||||
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
|
||||
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
|
||||
result = TxFeeEstimationService.getEstimatedTxVsize(outputValues, initialEstimatedTxVsize, txFeePerVbyte, btcWalletService);
|
||||
assertEquals(175, result);
|
||||
}
|
||||
|
||||
// FIXME @Bernard could you have a look?
|
||||
@Test
|
||||
@Ignore
|
||||
public void testGetEstimatedTxVsize_withLargeTx() throws InsufficientMoneyException {
|
||||
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
|
||||
int initialEstimatedTxVsize;
|
||||
Coin txFeePerVbyte;
|
||||
BtcWalletService btcWalletService = mock(BtcWalletService.class);
|
||||
int result;
|
||||
int realTxVsize;
|
||||
Coin txFee;
|
||||
|
||||
initialEstimatedTxVsize = 175;
|
||||
txFeePerVbyte = Coin.valueOf(10);
|
||||
realTxVsize = 1750;
|
||||
|
||||
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
|
||||
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
|
||||
|
||||
// repeated calls to getEstimatedFeeTxVsize do not work (returns 0 at second call in loop which cause test to fail)
|
||||
result = TxFeeEstimationService.getEstimatedTxVsize(outputValues, initialEstimatedTxVsize, txFeePerVbyte, btcWalletService);
|
||||
assertEquals(1750, result);
|
||||
}
|
||||
|
||||
// FIXME @Bernard could you have a look?
|
||||
@Test
|
||||
@Ignore
|
||||
public void testGetEstimatedTxVsize_withSmallTx() throws InsufficientMoneyException {
|
||||
List<Coin> outputValues = List.of(Coin.valueOf(2000), Coin.valueOf(3000));
|
||||
int initialEstimatedTxVsize;
|
||||
Coin txFeePerVbyte;
|
||||
BtcWalletService btcWalletService = mock(BtcWalletService.class);
|
||||
int result;
|
||||
int realTxVsize;
|
||||
Coin txFee;
|
||||
|
||||
initialEstimatedTxVsize = 1750;
|
||||
txFeePerVbyte = Coin.valueOf(10);
|
||||
realTxVsize = 175;
|
||||
|
||||
txFee = txFeePerVbyte.multiply(initialEstimatedTxVsize);
|
||||
when(btcWalletService.getEstimatedFeeTxVsize(outputValues, txFee)).thenReturn(realTxVsize);
|
||||
result = TxFeeEstimationService.getEstimatedTxVsize(outputValues, initialEstimatedTxVsize, txFeePerVbyte, btcWalletService);
|
||||
assertEquals(175, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInTolerance() {
|
||||
int estimatedSize;
|
||||
int txVsize;
|
||||
double tolerance;
|
||||
boolean result;
|
||||
|
||||
estimatedSize = 100;
|
||||
txVsize = 100;
|
||||
tolerance = 0.0001;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertTrue(result);
|
||||
|
||||
estimatedSize = 100;
|
||||
txVsize = 200;
|
||||
tolerance = 0.2;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertFalse(result);
|
||||
|
||||
estimatedSize = 120;
|
||||
txVsize = 100;
|
||||
tolerance = 0.2;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertTrue(result);
|
||||
|
||||
estimatedSize = 200;
|
||||
txVsize = 100;
|
||||
tolerance = 1;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertTrue(result);
|
||||
|
||||
estimatedSize = 201;
|
||||
txVsize = 100;
|
||||
tolerance = 1;
|
||||
result = TxFeeEstimationService.isInTolerance(estimatedSize, txVsize, tolerance);
|
||||
assertFalse(result);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.btc.nodes;
|
||||
|
||||
import bisq.core.btc.setup.WalletConfig;
|
||||
|
||||
import bisq.network.Socks5MultiDiscovery;
|
||||
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.PeerAddress;
|
||||
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class BtcNetworkConfigTest {
|
||||
private static final int MODE = 0;
|
||||
|
||||
private WalletConfig delegate;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
delegate = mock(WalletConfig.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProposePeersWhenProxyPresentAndNoPeers() {
|
||||
BtcNetworkConfig config = new BtcNetworkConfig(delegate, mock(NetworkParameters.class), MODE,
|
||||
mock(Socks5Proxy.class));
|
||||
config.proposePeers(Collections.emptyList());
|
||||
|
||||
verify(delegate, never()).setPeerNodes(any());
|
||||
verify(delegate).setDiscovery(any(Socks5MultiDiscovery.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProposePeersWhenProxyNotPresentAndNoPeers() {
|
||||
BtcNetworkConfig config = new BtcNetworkConfig(delegate, mock(NetworkParameters.class), MODE,
|
||||
null);
|
||||
config.proposePeers(Collections.emptyList());
|
||||
|
||||
verify(delegate, never()).setDiscovery(any(Socks5MultiDiscovery.class));
|
||||
verify(delegate, never()).setPeerNodes(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProposePeersWhenPeersPresent() {
|
||||
BtcNetworkConfig config = new BtcNetworkConfig(delegate, mock(NetworkParameters.class), MODE,
|
||||
null);
|
||||
config.proposePeers(Collections.singletonList(mock(PeerAddress.class)));
|
||||
|
||||
verify(delegate, never()).setDiscovery(any(Socks5MultiDiscovery.class));
|
||||
verify(delegate).setPeerNodes(any());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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.btc.nodes;
|
||||
|
||||
import bisq.core.btc.nodes.BtcNodeConverter.Facade;
|
||||
import bisq.core.btc.nodes.BtcNodes.BtcNode;
|
||||
|
||||
import bisq.network.DnsLookupException;
|
||||
|
||||
import org.bitcoinj.core.PeerAddress;
|
||||
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BtcNodeConverterTest {
|
||||
@Test
|
||||
public void testConvertOnionHost() {
|
||||
BtcNode node = mock(BtcNode.class);
|
||||
when(node.getOnionAddress()).thenReturn("aaa.onion");
|
||||
|
||||
//InetAddress inetAddress = mock(InetAddress.class);
|
||||
|
||||
Facade facade = mock(Facade.class);
|
||||
//when(facade.onionHostToInetAddress(any())).thenReturn(inetAddress);
|
||||
|
||||
PeerAddress peerAddress = new BtcNodeConverter(facade).convertOnionHost(node);
|
||||
// noinspection ConstantConditions
|
||||
assertEquals(node.getOnionAddress(), peerAddress.getHostname());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertClearNode() {
|
||||
final String ip = "192.168.0.1";
|
||||
|
||||
BtcNode node = mock(BtcNode.class);
|
||||
when(node.getHostNameOrAddress()).thenReturn(ip);
|
||||
|
||||
PeerAddress peerAddress = new BtcNodeConverter().convertClearNode(node);
|
||||
// noinspection ConstantConditions
|
||||
InetAddress inetAddress = peerAddress.getAddr();
|
||||
assertEquals(ip, inetAddress.getHostAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertWithTor() throws DnsLookupException {
|
||||
InetAddress expected = mock(InetAddress.class);
|
||||
|
||||
Facade facade = mock(Facade.class);
|
||||
when(facade.torLookup(any(), anyString())).thenReturn(expected);
|
||||
|
||||
BtcNode node = mock(BtcNode.class);
|
||||
when(node.getHostNameOrAddress()).thenReturn("aaa.onion");
|
||||
|
||||
PeerAddress peerAddress = new BtcNodeConverter(facade).convertWithTor(node, mock(Socks5Proxy.class));
|
||||
|
||||
// noinspection ConstantConditions
|
||||
assertEquals(expected, peerAddress.getAddr());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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.btc.nodes;
|
||||
|
||||
import bisq.core.btc.nodes.BtcNodes.BtcNode;
|
||||
|
||||
import org.bitcoinj.core.PeerAddress;
|
||||
|
||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BtcNodesRepositoryTest {
|
||||
@Test
|
||||
public void testGetPeerAddressesWhenClearNodes() {
|
||||
BtcNode node = mock(BtcNode.class);
|
||||
when(node.hasClearNetAddress()).thenReturn(true);
|
||||
|
||||
BtcNodeConverter converter = mock(BtcNodeConverter.class, RETURNS_DEEP_STUBS);
|
||||
BtcNodesRepository repository = new BtcNodesRepository(converter,
|
||||
Collections.singletonList(node));
|
||||
|
||||
List<PeerAddress> peers = repository.getPeerAddresses(null, false);
|
||||
|
||||
assertFalse(peers.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPeerAddressesWhenConverterReturnsNull() {
|
||||
BtcNodeConverter converter = mock(BtcNodeConverter.class);
|
||||
when(converter.convertClearNode(any())).thenReturn(null);
|
||||
|
||||
BtcNode node = mock(BtcNode.class);
|
||||
when(node.hasClearNetAddress()).thenReturn(true);
|
||||
|
||||
BtcNodesRepository repository = new BtcNodesRepository(converter,
|
||||
Collections.singletonList(node));
|
||||
|
||||
List<PeerAddress> peers = repository.getPeerAddresses(null, false);
|
||||
|
||||
verify(converter).convertClearNode(any());
|
||||
assertTrue(peers.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPeerAddressesWhenProxyAndClearNodes() {
|
||||
BtcNode node = mock(BtcNode.class);
|
||||
when(node.hasClearNetAddress()).thenReturn(true);
|
||||
|
||||
BtcNode onionNode = mock(BtcNode.class);
|
||||
when(node.hasOnionAddress()).thenReturn(true);
|
||||
|
||||
BtcNodeConverter converter = mock(BtcNodeConverter.class, RETURNS_DEEP_STUBS);
|
||||
BtcNodesRepository repository = new BtcNodesRepository(converter,
|
||||
Lists.newArrayList(node, onionNode));
|
||||
|
||||
List<PeerAddress> peers = repository.getPeerAddresses(mock(Socks5Proxy.class), true);
|
||||
|
||||
assertEquals(2, peers.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPeerAddressesWhenOnionNodesOnly() {
|
||||
BtcNode node = mock(BtcNode.class);
|
||||
when(node.hasClearNetAddress()).thenReturn(true);
|
||||
|
||||
BtcNode onionNode = mock(BtcNode.class);
|
||||
when(node.hasOnionAddress()).thenReturn(true);
|
||||
|
||||
BtcNodeConverter converter = mock(BtcNodeConverter.class, RETURNS_DEEP_STUBS);
|
||||
BtcNodesRepository repository = new BtcNodesRepository(converter,
|
||||
Lists.newArrayList(node, onionNode));
|
||||
|
||||
List<PeerAddress> peers = repository.getPeerAddresses(mock(Socks5Proxy.class), false);
|
||||
|
||||
assertEquals(1, peers.size());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.btc.nodes;
|
||||
|
||||
import bisq.core.btc.nodes.BtcNodes.BtcNode;
|
||||
import bisq.core.user.Preferences;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static bisq.core.btc.nodes.BtcNodes.BitcoinNodesOption.CUSTOM;
|
||||
import static bisq.core.btc.nodes.BtcNodes.BitcoinNodesOption.PUBLIC;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BtcNodesSetupPreferencesTest {
|
||||
@Test
|
||||
public void testSelectPreferredNodesWhenPublicOption() {
|
||||
Preferences delegate = mock(Preferences.class);
|
||||
when(delegate.getBitcoinNodesOptionOrdinal()).thenReturn(PUBLIC.ordinal());
|
||||
|
||||
BtcNodesSetupPreferences preferences = new BtcNodesSetupPreferences(delegate);
|
||||
List<BtcNode> nodes = preferences.selectPreferredNodes(mock(BtcNodes.class));
|
||||
|
||||
assertTrue(nodes.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectPreferredNodesWhenCustomOption() {
|
||||
Preferences delegate = mock(Preferences.class);
|
||||
when(delegate.getBitcoinNodesOptionOrdinal()).thenReturn(CUSTOM.ordinal());
|
||||
when(delegate.getBitcoinNodes()).thenReturn("aaa.onion,bbb.onion");
|
||||
|
||||
BtcNodesSetupPreferences preferences = new BtcNodesSetupPreferences(delegate);
|
||||
List<BtcNode> nodes = preferences.selectPreferredNodes(mock(BtcNodes.class));
|
||||
|
||||
assertEquals(2, nodes.size());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.btc.wallet;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public class RestrictionsTest {
|
||||
@Test
|
||||
public void testIsMinSpendableAmount() {
|
||||
Coin amount = null;
|
||||
Coin txFee = Coin.valueOf(20000);
|
||||
|
||||
amount = Coin.ZERO;
|
||||
assertFalse(Restrictions.isAboveDust(amount.subtract(txFee)));
|
||||
|
||||
amount = txFee;
|
||||
assertFalse(Restrictions.isAboveDust(amount.subtract(txFee)));
|
||||
|
||||
amount = Restrictions.getMinNonDustOutput();
|
||||
assertFalse(Restrictions.isAboveDust(amount.subtract(txFee)));
|
||||
|
||||
amount = txFee.add(Restrictions.getMinNonDustOutput());
|
||||
assertTrue(Restrictions.isAboveDust(amount.subtract(txFee)));
|
||||
|
||||
amount = txFee.add(Restrictions.getMinNonDustOutput()).add(Coin.valueOf(1));
|
||||
assertTrue(Restrictions.isAboveDust(amount.subtract(txFee)));
|
||||
}
|
||||
}
|
61
core/src/test/java/bisq/core/crypto/EncryptionTest.java
Normal file
61
core/src/test/java/bisq/core/crypto/EncryptionTest.java
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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.crypto;
|
||||
|
||||
import bisq.common.crypto.CryptoException;
|
||||
import bisq.common.crypto.KeyRing;
|
||||
import bisq.common.crypto.KeyStorage;
|
||||
import bisq.common.file.FileUtil;
|
||||
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
public class EncryptionTest {
|
||||
private static final Logger log = LoggerFactory.getLogger(EncryptionTest.class);
|
||||
private KeyRing keyRing;
|
||||
private File dir;
|
||||
|
||||
@Before
|
||||
public void setup() throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, CryptoException {
|
||||
|
||||
dir = File.createTempFile("temp_tests", "");
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
dir.delete();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
dir.mkdir();
|
||||
KeyStorage keyStorage = new KeyStorage(dir);
|
||||
keyRing = new KeyRing(keyStorage);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws IOException {
|
||||
FileUtil.deleteDirectory(dir);
|
||||
}
|
||||
|
||||
|
||||
}
|
89
core/src/test/java/bisq/core/crypto/SigTest.java
Normal file
89
core/src/test/java/bisq/core/crypto/SigTest.java
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.crypto;
|
||||
|
||||
import bisq.common.crypto.CryptoException;
|
||||
import bisq.common.crypto.KeyRing;
|
||||
import bisq.common.crypto.KeyStorage;
|
||||
import bisq.common.crypto.Sig;
|
||||
import bisq.common.file.FileUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class SigTest {
|
||||
private static final Logger log = LoggerFactory.getLogger(SigTest.class);
|
||||
private KeyRing keyRing;
|
||||
private File dir;
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
|
||||
dir = File.createTempFile("temp_tests", "");
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
dir.delete();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
dir.mkdir();
|
||||
KeyStorage keyStorage = new KeyStorage(dir);
|
||||
keyRing = new KeyRing(keyStorage);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws IOException {
|
||||
FileUtil.deleteDirectory(dir);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSignature() {
|
||||
long ts = System.currentTimeMillis();
|
||||
log.trace("start ");
|
||||
for (int i = 0; i < 100; i++) {
|
||||
String msg = String.valueOf(new Random().nextInt());
|
||||
String sig = null;
|
||||
try {
|
||||
sig = Sig.sign(keyRing.getSignatureKeyPair().getPrivate(), msg);
|
||||
} catch (CryptoException e) {
|
||||
log.error("sign failed");
|
||||
e.printStackTrace();
|
||||
assertTrue(false);
|
||||
}
|
||||
try {
|
||||
assertTrue(Sig.verify(keyRing.getSignatureKeyPair().getPublic(), msg, sig));
|
||||
} catch (CryptoException e) {
|
||||
log.error("verify failed");
|
||||
e.printStackTrace();
|
||||
assertTrue(false);
|
||||
}
|
||||
}
|
||||
log.trace("took {} ms.", System.currentTimeMillis() - ts);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package bisq.core.dao.governance.ballot;
|
||||
|
||||
import bisq.core.dao.governance.ballot.BallotListService.BallotListChangeListener;
|
||||
import bisq.core.dao.governance.period.PeriodService;
|
||||
import bisq.core.dao.governance.proposal.ProposalService;
|
||||
import bisq.core.dao.governance.proposal.ProposalValidatorProvider;
|
||||
import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload;
|
||||
|
||||
import bisq.common.persistence.PersistenceManager;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class BallotListServiceTest {
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testAddListenersWhenNewPayloadAdded() {
|
||||
// given
|
||||
ObservableList<ProposalPayload> payloads = FXCollections.observableArrayList();
|
||||
|
||||
ProposalService proposalService = mock(ProposalService.class);
|
||||
when(proposalService.getProposalPayloads()).thenReturn(payloads);
|
||||
|
||||
BallotListService service = new BallotListService(proposalService, mock(PeriodService.class),
|
||||
mock(ProposalValidatorProvider.class), mock(PersistenceManager.class));
|
||||
|
||||
BallotListChangeListener listener = mock(BallotListChangeListener.class);
|
||||
service.addListener(listener);
|
||||
|
||||
service.addListeners();
|
||||
|
||||
// when
|
||||
payloads.add(mock(ProposalPayload.class, RETURNS_DEEP_STUBS));
|
||||
|
||||
// then
|
||||
verify(listener).onListChanged(any());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package bisq.core.dao.governance.proposal;
|
||||
|
||||
import bisq.core.btc.wallet.WalletsManager;
|
||||
import bisq.core.dao.governance.period.PeriodService;
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
import bisq.common.crypto.PubKeyRing;
|
||||
import bisq.common.persistence.PersistenceManager;
|
||||
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class MyProposalListServiceTest {
|
||||
@Test
|
||||
public void canInstantiate() {
|
||||
P2PService p2PService = mock(P2PService.class);
|
||||
when(p2PService.getNumConnectedPeers()).thenReturn(new SimpleIntegerProperty(0));
|
||||
PersistenceManager persistenceManager = mock(PersistenceManager.class);
|
||||
MyProposalListService service = new MyProposalListService(p2PService,
|
||||
mock(DaoStateService.class),
|
||||
mock(PeriodService.class), mock(WalletsManager.class), persistenceManager, mock(PubKeyRing.class)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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.proposal;
|
||||
|
||||
import bisq.core.dao.governance.period.PeriodService;
|
||||
import bisq.core.dao.governance.proposal.storage.appendonly.ProposalStorageService;
|
||||
import bisq.core.dao.governance.proposal.storage.temp.TempProposalPayload;
|
||||
import bisq.core.dao.governance.proposal.storage.temp.TempProposalStorageService;
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
import bisq.core.dao.state.model.governance.DaoPhase;
|
||||
import bisq.core.dao.state.model.governance.Proposal;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
||||
import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService;
|
||||
import bisq.network.p2p.storage.persistence.ProtectedDataStoreService;
|
||||
|
||||
import javafx.collections.ListChangeListener;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
|
||||
/**
|
||||
* Tests of the P2PDataStorage::onRemoved callback behavior to ensure that the proper number of signal events occur.
|
||||
*/
|
||||
public class ProposalServiceP2PDataStorageListenerTest {
|
||||
private ProposalService proposalService;
|
||||
|
||||
@Mock
|
||||
private PeriodService periodService;
|
||||
|
||||
@Mock
|
||||
private DaoStateService daoStateService;
|
||||
|
||||
@Mock
|
||||
private ListChangeListener<Proposal> tempProposalListener;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
this.proposalService = new ProposalService(
|
||||
mock(P2PService.class),
|
||||
this.periodService,
|
||||
mock(ProposalStorageService.class),
|
||||
mock(TempProposalStorageService.class),
|
||||
mock(AppendOnlyDataStoreService.class),
|
||||
mock(ProtectedDataStoreService.class),
|
||||
this.daoStateService,
|
||||
mock(ProposalValidatorProvider.class),
|
||||
true);
|
||||
|
||||
// Create a state so that all added/removed Proposals will actually update the tempProposals list.
|
||||
when(this.periodService.isInPhase(anyInt(), any(DaoPhase.Phase.class))).thenReturn(true);
|
||||
when(this.daoStateService.isParseBlockChainComplete()).thenReturn(false);
|
||||
}
|
||||
|
||||
private static ProtectedStorageEntry buildProtectedStorageEntry() {
|
||||
ProtectedStorageEntry protectedStorageEntry = mock(ProtectedStorageEntry.class);
|
||||
TempProposalPayload tempProposalPayload = mock(TempProposalPayload.class);
|
||||
Proposal tempProposal = mock(Proposal.class);
|
||||
when(protectedStorageEntry.getProtectedStoragePayload()).thenReturn(tempProposalPayload);
|
||||
when(tempProposalPayload.getProposal()).thenReturn(tempProposal);
|
||||
|
||||
return protectedStorageEntry;
|
||||
}
|
||||
|
||||
// TESTCASE: If an onRemoved callback is called which does not remove anything the tempProposals listeners
|
||||
// are not signaled.
|
||||
@Test
|
||||
public void onRemoved_noSignalIfNoChange() {
|
||||
this.proposalService.onRemoved(Collections.singletonList(mock(ProtectedStorageEntry.class)));
|
||||
|
||||
verify(this.tempProposalListener, never()).onChanged(any());
|
||||
}
|
||||
|
||||
// TESTCASE: If an onRemoved callback is called with 1 element AND it creates a remove of 1 element, the tempProposal
|
||||
// listeners are signaled once.
|
||||
@Test
|
||||
public void onRemoved_signalOnceOnOneChange() {
|
||||
ProtectedStorageEntry one = buildProtectedStorageEntry();
|
||||
this.proposalService.onAdded(Collections.singletonList(one));
|
||||
this.proposalService.getTempProposals().addListener(this.tempProposalListener);
|
||||
|
||||
this.proposalService.onRemoved(Collections.singletonList(one));
|
||||
|
||||
verify(this.tempProposalListener).onChanged(any());
|
||||
}
|
||||
|
||||
// TESTCASE: If an onRemoved callback is called with 2 elements AND it creates a remove of 2 elements, the
|
||||
// tempProposal listeners are signaled once.
|
||||
@Test
|
||||
public void onRemoved_signalOnceOnMultipleChanges() {
|
||||
ProtectedStorageEntry one = buildProtectedStorageEntry();
|
||||
ProtectedStorageEntry two = buildProtectedStorageEntry();
|
||||
this.proposalService.onAdded(Arrays.asList(one, two));
|
||||
this.proposalService.getTempProposals().addListener(this.tempProposalListener);
|
||||
|
||||
this.proposalService.onRemoved(Arrays.asList(one, two));
|
||||
|
||||
verify(this.tempProposalListener).onChanged(any());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.proposal.param;
|
||||
|
||||
import bisq.core.dao.governance.param.Param;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.util.coin.BsqFormatter;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ChangeParamValidatorTest {
|
||||
@Before
|
||||
public void setup() {
|
||||
Res.setup();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetChangeValidationResult() throws ParamValidationException {
|
||||
ChangeParamValidator changeParamValidator = new ChangeParamValidator(null, null, new BsqFormatter());
|
||||
try {
|
||||
changeParamValidator.validationChange(0, 0, 2, 2, Param.UNDEFINED);
|
||||
Assert.fail();
|
||||
} catch (ParamValidationException e) {
|
||||
Assert.assertEquals(e.getError(), ParamValidationException.ERROR.SAME);
|
||||
}
|
||||
|
||||
try {
|
||||
changeParamValidator.validationChange(0, 1, 2, 2, Param.UNDEFINED);
|
||||
Assert.fail();
|
||||
} catch (ParamValidationException e) {
|
||||
Assert.assertEquals(e.getError(), ParamValidationException.ERROR.NO_CHANGE_POSSIBLE);
|
||||
}
|
||||
|
||||
try {
|
||||
changeParamValidator.validationChange(0, -1, 2, 2, Param.UNDEFINED);
|
||||
Assert.fail();
|
||||
} catch (ParamValidationException e) {
|
||||
Assert.assertEquals(e.getError(), ParamValidationException.ERROR.NO_CHANGE_POSSIBLE);
|
||||
}
|
||||
|
||||
try {
|
||||
changeParamValidator.validationChange(2, 4, 2, 1.1, Param.UNDEFINED);
|
||||
Assert.fail();
|
||||
} catch (ParamValidationException e) {
|
||||
Assert.assertEquals(e.getError(), ParamValidationException.ERROR.TOO_HIGH);
|
||||
}
|
||||
|
||||
try {
|
||||
changeParamValidator.validationChange(4, 2, 1.5, 2, Param.UNDEFINED);
|
||||
Assert.fail();
|
||||
} catch (ParamValidationException e) {
|
||||
Assert.assertEquals(e.getError(), ParamValidationException.ERROR.TOO_LOW);
|
||||
}
|
||||
|
||||
changeParamValidator.validationChange(4, 2, 2, 2, Param.UNDEFINED);
|
||||
changeParamValidator.validationChange(2, 4, 2, 2, Param.UNDEFINED);
|
||||
changeParamValidator.validationChange(0, 1, 0, 0, Param.UNDEFINED);
|
||||
changeParamValidator.validationChange(0, -1, 0, 0, Param.UNDEFINED);
|
||||
changeParamValidator.validationChange(-1, 0, 0, 0, Param.UNDEFINED);
|
||||
changeParamValidator.validationChange(1, 0, 0, 0, Param.UNDEFINED);
|
||||
}
|
||||
}
|
205
core/src/test/java/bisq/core/dao/node/full/BlockParserTest.java
Normal file
205
core/src/test/java/bisq/core/dao/node/full/BlockParserTest.java
Normal file
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* 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.node.full;
|
||||
|
||||
// not converting this test because it is already ignored.
|
||||
// Intro to jmockit can be found at http://jmockit.github.io/tutorial/Mocking.html
|
||||
//@Ignore
|
||||
/*
|
||||
public class BlockParserTest {
|
||||
// @Tested classes are instantiated automatically when needed in a test case,
|
||||
// using injection where possible, see http://jmockit.github.io/tutorial/Mocking.html#tested
|
||||
// To force instantiate earlier, use availableDuringSetup
|
||||
@Tested(fullyInitialized = true, availableDuringSetup = true)
|
||||
BlockParser blockParser;
|
||||
|
||||
@Tested(fullyInitialized = true, availableDuringSetup = true)
|
||||
DaoStateService daoStateService;
|
||||
|
||||
// @Injectable are mocked resources used to for injecting into @Tested classes
|
||||
// The naming of these resources doesn't matter, any resource that fits will be used for injection
|
||||
|
||||
// Used by daoStateService
|
||||
@Injectable
|
||||
PersistenceProtoResolver persistenceProtoResolver;
|
||||
@Injectable
|
||||
File storageDir;
|
||||
@Injectable
|
||||
String genesisTxId = "genesisTxId";
|
||||
@Injectable
|
||||
int genesisBlockHeight = 200;
|
||||
|
||||
// Used by fullNodeParser
|
||||
@Injectable
|
||||
RpcService rpcService;
|
||||
@Tested(fullyInitialized = true, availableDuringSetup = true)
|
||||
DaoStateService writeModel;
|
||||
@Tested(fullyInitialized = true, availableDuringSetup = true)
|
||||
TxParser txParser;
|
||||
|
||||
//FIXME
|
||||
@Test
|
||||
public void testIsBsqTx() {
|
||||
// Setup a basic transaction with two inputs
|
||||
int height = 200;
|
||||
String hash = "abc123";
|
||||
long time = new Date().getTime();
|
||||
final List<TxInput> inputs = asList(new TxInput("tx1", 0, null),
|
||||
new TxInput("tx1", 1, null));
|
||||
final List<RawTxOutput> outputs = asList(new RawTxOutput(0, 101, "tx1", null, null, null, height));
|
||||
RawTx rawTx = new RawTx("vo", height, hash, time,
|
||||
ImmutableList.copyOf(inputs),
|
||||
ImmutableList.copyOf(outputs));
|
||||
|
||||
// Return one spendable txoutputs with value, for three test cases
|
||||
// 1) - null, 0 -> not BSQ transaction
|
||||
// 2) - 100, null -> BSQ transaction
|
||||
// 3) - 0, 100 -> BSQ transaction
|
||||
new Expectations(daoStateService) {{
|
||||
// Expectations can be recorded on mocked instances, either with specific matching arguments or catch all
|
||||
// http://jmockit.github.io/tutorial/Mocking.html#results
|
||||
// Results are returned in the order they're recorded, so in this case for the first call to
|
||||
// getSpendableTxOutput("tx1", 0) the return value will be Optional.empty()
|
||||
// for the second call the return is Optional.of(new TxOutput(0,... and so on
|
||||
daoStateService.getUnspentTxOutput(new TxOutputKey("tx1", 0));
|
||||
result = Optional.empty();
|
||||
result = Optional.of(new RawTxOutput(0, 100, "txout1", null, null, null, height));
|
||||
result = Optional.of(new RawTxOutput(0, 0, "txout1", null, null, null, height));
|
||||
|
||||
daoStateService.getUnspentTxOutput(new TxOutputKey("tx1", 1));
|
||||
result = Optional.of(new RawTxOutput(0, 0, "txout2", null, null, null, height));
|
||||
result = Optional.empty();
|
||||
result = Optional.of(new RawTxOutput(0, 100, "txout2", null, null, null, height));
|
||||
}};
|
||||
String genesisTxId = "genesisTxId";
|
||||
int blockHeight = 200;
|
||||
String blockHash = "abc123";
|
||||
Coin genesisTotalSupply = Coin.parseCoin("2.5");
|
||||
|
||||
// First time there is no BSQ value to spend so it's not a bsq transaction
|
||||
assertFalse(txParser.findTx(rawTx, genesisTxId, blockHeight, genesisTotalSupply).isPresent());
|
||||
// Second time there is BSQ in the first txout
|
||||
assertTrue(txParser.findTx(rawTx, genesisTxId, blockHeight, genesisTotalSupply).isPresent());
|
||||
// Third time there is BSQ in the second txout
|
||||
assertTrue(txParser.findTx(rawTx, genesisTxId, blockHeight, genesisTotalSupply).isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseBlocks() {
|
||||
// Setup blocks to test, starting before genesis
|
||||
// Only the transactions related to bsq are relevant, no checks are done on correctness of blocks or other txs
|
||||
// so hashes and most other data don't matter
|
||||
long time = new Date().getTime();
|
||||
int genesisHeight = 200;
|
||||
int startHeight = 199;
|
||||
int headHeight = 201;
|
||||
Coin issuance = Coin.parseCoin("2.5");
|
||||
RawTransaction genTx = new RawTransaction("gen block hash", 0, 0L, 0L, genesisTxId);
|
||||
|
||||
// Blockhashes
|
||||
String bh199 = "blockhash199";
|
||||
String bh200 = "blockhash200";
|
||||
String bh201 = "blockhash201";
|
||||
|
||||
// Block 199
|
||||
String cbId199 = "cbid199";
|
||||
RawTransaction tx199 = new RawTransaction(bh199, 0, 0L, 0L, cbId199);
|
||||
RawTx cbTx199 = new RawTx(cbId199, 199, bh199, time,
|
||||
ImmutableList.copyOf(new ArrayList<TxInput>()),
|
||||
ImmutableList.copyOf(asList(new RawTxOutput(0, 25, cbId199, null, null, null, 199))));
|
||||
RawBlock block199 = new RawBlock(bh199, 10, 10, 199, 2, "root", asList(tx199), time, Long.parseLong("1234"), "bits", BigDecimal.valueOf(1), "chainwork", "previousBlockHash", bh200);
|
||||
|
||||
// Genesis Block
|
||||
String cbId200 = "cbid200";
|
||||
RawTransaction tx200 = new RawTransaction(bh200, 0, 0L, 0L, cbId200);
|
||||
RawTx cbTx200 = new RawTx(cbId200, 200, bh200, time,
|
||||
ImmutableList.copyOf(new ArrayList<TxInput>()),
|
||||
ImmutableList.copyOf(asList(new RawTxOutput(0, 25, cbId200, null, null, null, 200))));
|
||||
RawTx genesisTx = new RawTx(genesisTxId, 200, bh200, time,
|
||||
ImmutableList.copyOf(asList(new TxInput("someoldtx", 0, null))),
|
||||
ImmutableList.copyOf(asList(new RawTxOutput(0, issuance.getValue(), genesisTxId, null, null, null, 200))));
|
||||
RawBlock block200 = new RawBlock(bh200, 10, 10, 200, 2, "root", asList(tx200, genTx), time, Long.parseLong("1234"), "bits", BigDecimal.valueOf(1), "chainwork", bh199, bh201);
|
||||
|
||||
// Block 201
|
||||
// Make a bsq transaction
|
||||
String cbId201 = "cbid201";
|
||||
String bsqTx1Id = "bsqtx1";
|
||||
RawTransaction tx201 = new RawTransaction(bh201, 0, 0L, 0L, cbId201);
|
||||
RawTransaction txbsqtx1 = new RawTransaction(bh201, 0, 0L, 0L, bsqTx1Id);
|
||||
long bsqTx1Value1 = Coin.parseCoin("2.4").getValue();
|
||||
long bsqTx1Value2 = Coin.parseCoin("0.04").getValue();
|
||||
RawTx cbTx201 = new RawTx(cbId201, 201, bh201, time,
|
||||
ImmutableList.copyOf(new ArrayList<TxInput>()),
|
||||
ImmutableList.copyOf(asList(new RawTxOutput(0, 25, cbId201, null, null, null, 201))));
|
||||
RawTx bsqTx1 = new RawTx(bsqTx1Id, 201, bh201, time,
|
||||
ImmutableList.copyOf(asList(new TxInput(genesisTxId, 0, null))),
|
||||
ImmutableList.copyOf(asList(new RawTxOutput(0, bsqTx1Value1, bsqTx1Id, null, null, null, 201),
|
||||
new RawTxOutput(1, bsqTx1Value2, bsqTx1Id, null, null, null, 201))));
|
||||
RawBlock block201 = new RawBlock(bh201, 10, 10, 201, 2, "root", asList(tx201, txbsqtx1), time, Long.parseLong("1234"), "bits", BigDecimal.valueOf(1), "chainwork", bh200, "nextBlockHash");
|
||||
|
||||
// TODO update test with new API
|
||||
/*
|
||||
new Expectations(rpcService) {{
|
||||
rpcService.requestBlock(199);
|
||||
result = block199;
|
||||
rpcService.requestBlock(200);
|
||||
result = block200;
|
||||
rpcService.requestBlock(201);
|
||||
result = block201;
|
||||
|
||||
rpcService.requestTx(cbId199, 199);
|
||||
result = cbTx199;
|
||||
rpcService.requestTx(cbId200, genesisHeight);
|
||||
result = cbTx200;
|
||||
rpcService.requestTx(genesisTxId, genesisHeight);
|
||||
result = genesisTx;
|
||||
rpcService.requestTx(cbId201, 201);
|
||||
result = cbTx201;
|
||||
rpcService.requestTx(bsqTx1Id, 201);
|
||||
result = bsqTx1;
|
||||
}};
|
||||
|
||||
// Running parseBlocks to build the bsq blockchain
|
||||
fullNodeParser.parseBlocks(startHeight, headHeight, block -> {
|
||||
});
|
||||
*/
|
||||
|
||||
// Verify that the genesis tx has been added to the bsq blockchain with the correct issuance amount
|
||||
/* assertTrue(daoStateService.getGenesisTx().get() == genesisTx);
|
||||
assertTrue(daoStateService.getGenesisTotalSupply().getValue() == issuance.getValue());
|
||||
|
||||
// And that other txs are not added
|
||||
assertFalse(daoStateService.containsTx(cbId199));
|
||||
assertFalse(daoStateService.containsTx(cbId200));
|
||||
assertFalse(daoStateService.containsTx(cbId201));
|
||||
|
||||
// But bsq txs are added
|
||||
assertTrue(daoStateService.containsTx(bsqTx1Id));
|
||||
TxOutput bsqOut1 = daoStateService.getUnspentAndMatureTxOutput(bsqTx1Id, 0).get();
|
||||
assertTrue(daoStateService.isUnspent(bsqOut1));
|
||||
assertTrue(bsqOut1.getValue() == bsqTx1Value1);
|
||||
TxOutput bsqOut2 = daoStateService.getUnspentAndMatureTxOutput(bsqTx1Id, 1).get();
|
||||
assertTrue(daoStateService.isUnspent(bsqOut2));
|
||||
assertTrue(bsqOut2.getValue() == bsqTx1Value2);
|
||||
assertFalse(daoStateService.isTxOutputSpendable(genesisTxId, 0));
|
||||
assertTrue(daoStateService.isTxOutputSpendable(bsqTx1Id, 0));
|
||||
assertTrue(daoStateService.isTxOutputSpendable(bsqTx1Id, 1));
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
140
core/src/test/java/bisq/core/dao/node/full/RpcServiceTest.java
Normal file
140
core/src/test/java/bisq/core/dao/node/full/RpcServiceTest.java
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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.node.full;
|
||||
|
||||
import bisq.core.dao.node.full.rpc.dto.RawDtoInput;
|
||||
import bisq.core.dao.node.full.rpc.dto.DtoSignatureScript;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class RpcServiceTest {
|
||||
private static final String SIGNATURE = "3045" +
|
||||
"022100b6c2fa10587d6fed3a0eecfd098b160f69a850beca139fe03ef65bec4cba1c5b" +
|
||||
"02204a833a16c22bbd32722243ea3270e672f646ee9406e8797e11093951e92efbd5";
|
||||
private static final String SIGNATURE_1 = "3044" +
|
||||
"02201f00d9a4aab1a3a239f1ad95a910092c0c55423480d609eaad4599cf7ecb7f48" +
|
||||
"0220668b1a9cf5624b1c4ece6da3f64bc6021e509f588ae1006601acd8a9f83b3576";
|
||||
private static final String SIGNATURE_2 = "3045" +
|
||||
"022100982eca77a72a2bdba51b9231afd4521400bee1bb7830634eb26db2b0c621bc46" +
|
||||
"022073d7325916e2b5ceb1d2e510a5161fd9115105a8dafa94068864624bb10d190e";
|
||||
private static final String PUB_KEY =
|
||||
"03dcca91c2ec7229f1b4f4c4f664c92d3303dddef8d38736f6a7f28de16f3ce416";
|
||||
private static final String PUB_KEY_1 =
|
||||
"0229713ad5c604c585128b3a5da6de20d78fc33bd3b595e9991f4c0e1fee99f845";
|
||||
private static final String PUB_KEY_2 =
|
||||
"0398ad45a74bf5a5c5a8ec31de6815d2e805a23e68c0f8001770e74bc4c17c5b31";
|
||||
private static final String MULTISIG_REDEEM_SCRIPT_HEX =
|
||||
"5221" + PUB_KEY_1 + "21" + PUB_KEY_2 + "52ae"; // OP_2 pub1 pub2 OP_2 OP_CHECKMULTISIG
|
||||
private static final String P2WPKH_REDEEM_SCRIPT_HEX =
|
||||
"0014" + "9bc809698674ec7c01d35d438e9d0de1aa87b6c8"; // 0 hash160
|
||||
private static final String P2WSH_REDEEM_SCRIPT_HEX =
|
||||
"0020" + "223d978073802f79e6ecdc7591e5dc1f0ea7030d6466f73c6b90391bc72e886f"; // 0 hash256
|
||||
|
||||
@Test
|
||||
public void testExtractPubKeyAsHex_coinbase() {
|
||||
checkExtractPubKeyAsHexReturnsNull(new RawDtoInput());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractPubKeyAsHex_P2PK() {
|
||||
var input = rawInput(SIGNATURE + "[ALL]");
|
||||
checkExtractPubKeyAsHexReturnsNull(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractPubKeyAsHex_P2PKH() {
|
||||
var input = rawInput(SIGNATURE + "[ALL] " + PUB_KEY);
|
||||
assertEquals(PUB_KEY, RpcService.extractPubKeyAsHex(input, true));
|
||||
assertEquals(PUB_KEY, RpcService.extractPubKeyAsHex(input, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractPubKeyAsHex_P2WPKH() {
|
||||
var input = rawInput("", SIGNATURE + "01", PUB_KEY);
|
||||
assertEquals(PUB_KEY, RpcService.extractPubKeyAsHex(input, true));
|
||||
assertNull(RpcService.extractPubKeyAsHex(input, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractPubKeyAsHex_P2SH_P2WPKH() {
|
||||
var input = rawInput(P2WPKH_REDEEM_SCRIPT_HEX, SIGNATURE + "01", PUB_KEY);
|
||||
assertEquals(PUB_KEY, RpcService.extractPubKeyAsHex(input, true));
|
||||
assertNull(RpcService.extractPubKeyAsHex(input, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractPubKeyAsHex_P2PKH_nonDefaultSighash() {
|
||||
var input = rawInput(SIGNATURE + "[SINGLE|ANYONECANPAY] " + PUB_KEY);
|
||||
checkExtractPubKeyAsHexReturnsNull(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractPubKeyAsHex_P2WPKH_nonDefaultSighash() {
|
||||
var input = rawInput("", SIGNATURE + "82", PUB_KEY);
|
||||
checkExtractPubKeyAsHexReturnsNull(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractPubKeyAsHex_P2SH_P2WPKH_nonDefaultSighash() {
|
||||
var input = rawInput(P2WPKH_REDEEM_SCRIPT_HEX, SIGNATURE + "82", PUB_KEY);
|
||||
checkExtractPubKeyAsHexReturnsNull(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractPubKeyAsHex_P2SH_multisig() {
|
||||
var input = rawInput("0 " + SIGNATURE_1 + "[ALL] " + SIGNATURE_2 + "[ALL] " + MULTISIG_REDEEM_SCRIPT_HEX);
|
||||
checkExtractPubKeyAsHexReturnsNull(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractPubKeyAsHex_P2SH_multisig_nonDefaultSighash() {
|
||||
var input = rawInput("0 " + SIGNATURE_1 + "[ALL] " + SIGNATURE_2 + "[NONE] " + MULTISIG_REDEEM_SCRIPT_HEX);
|
||||
checkExtractPubKeyAsHexReturnsNull(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractPubKeyAsHex_P2WSH_multisig() {
|
||||
var input = rawInput("", "", SIGNATURE_1 + "01", SIGNATURE_2 + "01", MULTISIG_REDEEM_SCRIPT_HEX);
|
||||
checkExtractPubKeyAsHexReturnsNull(input);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractPubKeyAsHex_P2SH_P2WSH_multisig() {
|
||||
var input = rawInput(P2WSH_REDEEM_SCRIPT_HEX, "", SIGNATURE_1 + "01", SIGNATURE_2 + "01", MULTISIG_REDEEM_SCRIPT_HEX);
|
||||
checkExtractPubKeyAsHexReturnsNull(input);
|
||||
}
|
||||
|
||||
private static void checkExtractPubKeyAsHexReturnsNull(RawDtoInput input) {
|
||||
assertNull(RpcService.extractPubKeyAsHex(input, true));
|
||||
assertNull(RpcService.extractPubKeyAsHex(input, false));
|
||||
}
|
||||
|
||||
private static RawDtoInput rawInput(String asm, String... txInWitness) {
|
||||
var input = new RawDtoInput();
|
||||
var scriptSig = new DtoSignatureScript();
|
||||
scriptSig.setAsm(asm);
|
||||
input.setScriptSig(scriptSig);
|
||||
input.setTxInWitness(txInWitness.length > 0 ? List.of(txInWitness) : null);
|
||||
return input;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* 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.node.full.rpc;
|
||||
|
||||
import bisq.core.dao.node.full.rpc.dto.RawDtoBlock;
|
||||
import bisq.core.dao.node.full.rpc.dto.RawDtoTransaction;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
|
||||
import com.googlecode.jsonrpc4j.HttpException;
|
||||
import com.googlecode.jsonrpc4j.JsonRpcClientException;
|
||||
import com.googlecode.jsonrpc4j.RequestIDGenerator;
|
||||
import kotlin.text.Charsets;
|
||||
|
||||
public class BitcoindClientTest {
|
||||
private static final String TEST_BLOCK_HASH = "015f37a20d517645a11a6cdd316049f41bc77b4a4057b2dd092114b78147f42c";
|
||||
private static final String TEST_BLOCK_VERBOSITY_0 = readFromResourcesUnPrettified("getblock-result-verbosity-0.txt");
|
||||
private static final String TEST_BLOCK_VERBOSITY_1 = readFromResourcesUnPrettified("getblock-result-verbosity-1.json");
|
||||
private static final String TEST_BLOCK_VERBOSITY_2 = readFromResourcesUnPrettified("getblock-result-verbosity-2.json");
|
||||
private static final String TEST_NETWORK_INFO = readFromResourcesUnPrettified("getnetworkinfo-result.json");
|
||||
|
||||
private BitcoindClient client;
|
||||
private int mockResponseCode = 200;
|
||||
private boolean canConnect = true;
|
||||
private ByteArrayInputStream mockResponse;
|
||||
private ByteArrayInputStream mockErrorResponse;
|
||||
private ByteArrayOutputStream mockOutputStream = new ByteArrayOutputStream();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
var mockURLStreamHandler = mock(MyURLStreamHandler.class);
|
||||
var mockRequestIDGenerator = mock(RequestIDGenerator.class);
|
||||
|
||||
client = BitcoindClient.builder()
|
||||
.rpcHost("127.0.0.1")
|
||||
.rpcPort(18443)
|
||||
.rpcUser("bisqdao")
|
||||
.rpcPassword("bsq")
|
||||
.urlStreamHandler(mockURLStreamHandler)
|
||||
.requestIDGenerator(mockRequestIDGenerator)
|
||||
.build();
|
||||
|
||||
when(mockURLStreamHandler.openConnection(any(), any())).then(inv -> {
|
||||
var connection = mock(HttpURLConnection.class);
|
||||
if (canConnect) {
|
||||
when(connection.getOutputStream()).thenReturn(mockOutputStream);
|
||||
if (mockResponseCode < 400) {
|
||||
when(connection.getInputStream()).thenReturn(mockResponse);
|
||||
} else {
|
||||
when(connection.getInputStream()).thenThrow(IOException.class);
|
||||
when(connection.getErrorStream()).thenReturn(mockErrorResponse);
|
||||
}
|
||||
} else {
|
||||
doThrow(ConnectException.class).when(connection).connect();
|
||||
}
|
||||
return connection;
|
||||
});
|
||||
when(mockRequestIDGenerator.generateID()).thenReturn("987654321");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBlockCount() throws Exception {
|
||||
var expectedRequest = toJson("{'id':'987654321','jsonrpc':'2.0','method':'getblockcount','params':[]}");
|
||||
mockResponse = toJsonIS("{'result':'150','error':null,'id':'123456789'}");
|
||||
|
||||
assertEquals((Integer) 150, client.getBlockCount());
|
||||
assertEquals(expectedRequest, mockOutputStream.toString(UTF_8));
|
||||
}
|
||||
|
||||
@Test(expected = ConnectException.class)
|
||||
public void testGetBlockCount_noConnection() throws Exception {
|
||||
canConnect = false;
|
||||
|
||||
client.getBlockCount();
|
||||
}
|
||||
|
||||
@Test(expected = HttpException.class)
|
||||
public void testGetBlockCount_wrongCredentials() throws Exception {
|
||||
mockResponseCode = 401;
|
||||
// mockResponseCustomHeaders.put("WWW-Authenticate", "[Basic realm=\"jsonrpc\"]");
|
||||
|
||||
client.getBlockCount();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBlockHash() throws Exception {
|
||||
var expectedRequest = toJson("{'id':'987654321','jsonrpc':'2.0','method':'getblockhash','params':[139]}");
|
||||
mockResponse = toJsonIS("{'result':'" + TEST_BLOCK_HASH + "','error':null,'id':'123456789'}");
|
||||
|
||||
assertEquals(TEST_BLOCK_HASH, client.getBlockHash(139));
|
||||
assertEquals(expectedRequest, mockOutputStream.toString(UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBestBlockHash() throws Exception {
|
||||
var expectedRequest = toJson("{'id':'987654321','jsonrpc':'2.0','method':'getbestblockhash','params':[]}");
|
||||
mockResponse = toJsonIS("{'result':'" + TEST_BLOCK_HASH + "','error':null,'id':'123456789'}");
|
||||
|
||||
assertEquals(TEST_BLOCK_HASH, client.getBestBlockHash());
|
||||
assertEquals(expectedRequest, mockOutputStream.toString(UTF_8));
|
||||
}
|
||||
|
||||
@Test(expected = JsonRpcClientException.class)
|
||||
public void testGetBlockHash_heightOutOfRange() throws Exception {
|
||||
mockResponseCode = 500;
|
||||
mockErrorResponse = toJsonIS("{'result':null,'error':{'code':-8,'message':'Block height out of range'},'id':'123456789'}");
|
||||
|
||||
client.getBlockHash(151);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBlock_verbosity_0() throws Exception {
|
||||
doTestGetBlock(0, "\"" + TEST_BLOCK_VERBOSITY_0 + "\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBlock_verbosity_1() throws Exception {
|
||||
doTestGetBlock(1, TEST_BLOCK_VERBOSITY_1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBlock_verbosity_2() throws Exception {
|
||||
doTestGetBlock(2, TEST_BLOCK_VERBOSITY_2);
|
||||
}
|
||||
|
||||
private void doTestGetBlock(int verbosity, String blockJson) throws Exception {
|
||||
var expectedRequest = toJson("{'id':'987654321','jsonrpc':'2.0','method':'getblock','params':['"
|
||||
+ TEST_BLOCK_HASH + "'," + verbosity + "]}");
|
||||
mockResponse = toJsonIS("{'result':" + blockJson + ",'error':null,'id':'123456789'}");
|
||||
|
||||
var block = client.getBlock(TEST_BLOCK_HASH, verbosity);
|
||||
var blockJsonRoundTripped = new ObjectMapper().writeValueAsString(block);
|
||||
|
||||
assertEquals(verbosity == 0, block instanceof RawDtoBlock.Summarized);
|
||||
assertEquals(verbosity == 1, block.getTx() != null &&
|
||||
block.getTx().stream().allMatch(tx -> tx instanceof RawDtoTransaction.Summarized));
|
||||
|
||||
assertEquals(blockJson, blockJsonRoundTripped);
|
||||
assertEquals(expectedRequest, mockOutputStream.toString(UTF_8));
|
||||
}
|
||||
|
||||
@Test(expected = JsonRpcClientException.class)
|
||||
public void testGetBlock_blockNotFound() throws Exception {
|
||||
mockResponseCode = 500;
|
||||
mockErrorResponse = toJsonIS("{'result':null,'error':{'code':-5,'message':'Block not found'},'id':'123456789'}");
|
||||
|
||||
client.getBlock(TEST_BLOCK_HASH.replace('f', 'e'), 2);
|
||||
}
|
||||
|
||||
@Test(expected = JsonRpcClientException.class)
|
||||
public void testGetBlock_malformedHash() throws Exception {
|
||||
mockResponseCode = 500;
|
||||
mockErrorResponse = toJsonIS("{'result':null,'error':{'code':-8,'message':'blockhash must be of length 64 " +
|
||||
"(not 3, for \\'foo\\')'},'id':'123456789'}");
|
||||
|
||||
client.getBlock("foo", 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNetworkInfo() throws Exception {
|
||||
var expectedRequest = toJson("{'id':'987654321','jsonrpc':'2.0','method':'getnetworkinfo','params':[]}");
|
||||
mockResponse = toJsonIS("{'result':" + TEST_NETWORK_INFO + ",'error':null,'id':'123456789'}");
|
||||
|
||||
var networkInfo = client.getNetworkInfo();
|
||||
var networkInfoRoundTripped = new ObjectMapper().writeValueAsString(networkInfo);
|
||||
var expectedNetworkInfoStr = TEST_NETWORK_INFO.replace("MY_CUSTOM_SERVICE", "UNKNOWN");
|
||||
|
||||
assertEquals(expectedNetworkInfoStr, networkInfoRoundTripped);
|
||||
assertEquals(expectedRequest, mockOutputStream.toString(UTF_8));
|
||||
}
|
||||
|
||||
private static String toJson(String json) {
|
||||
return json.replace("'", "\"").replace("\\\"", "'");
|
||||
}
|
||||
|
||||
private static ByteArrayInputStream toJsonIS(String json) {
|
||||
return new ByteArrayInputStream(toJson(json).getBytes(UTF_8));
|
||||
}
|
||||
|
||||
private static String readFromResourcesUnPrettified(String resourceName) {
|
||||
try {
|
||||
var path = Paths.get(BitcoindClientTest.class.getResource(resourceName).toURI());
|
||||
return new String(Files.readAllBytes(path), Charsets.UTF_8).replaceAll("(\\s+\\B|\\B\\s+|\\v)", "");
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class MyURLStreamHandler extends URLStreamHandler {
|
||||
@Override
|
||||
public abstract URLConnection openConnection(URL u, Proxy p);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* 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.node.full.rpc;
|
||||
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class BitcoindDaemonTest {
|
||||
private BitcoindDaemon daemon;
|
||||
private int acceptAnotherCount;
|
||||
private CountDownLatch errorHandlerLatch = new CountDownLatch(1);
|
||||
private Consumer<Throwable> errorHandler = mock(ThrowableConsumer.class);
|
||||
private BitcoindDaemon.BlockListener blockListener = mock(BitcoindDaemon.BlockListener.class);
|
||||
private Socket socket = mock(Socket.class);
|
||||
private volatile boolean socketClosed;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
var serverSocket = mock(ServerSocket.class);
|
||||
|
||||
when(serverSocket.accept()).then(invocation -> waitToAccept(() -> {
|
||||
if (socketClosed) {
|
||||
throw new SocketException();
|
||||
}
|
||||
return socket;
|
||||
}));
|
||||
doAnswer((VoidAnswer) invocation -> {
|
||||
socketClosed = true;
|
||||
acceptAnother(1);
|
||||
}).when(serverSocket).close();
|
||||
|
||||
doAnswer((VoidAnswer) invocation -> errorHandlerLatch.countDown()).when(errorHandler).accept(any());
|
||||
|
||||
daemon = new BitcoindDaemon(serverSocket, errorHandler);
|
||||
daemon.setBlockListener(blockListener);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
daemon.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoBlocksMissedDuringFloodOfIncomingBlocks() throws Exception {
|
||||
var latch = new CountDownLatch(1); // to block all the daemon worker threads until shutdown, as if stuck
|
||||
|
||||
doAnswer((VoidAnswer) invocation -> latch.await()).when(blockListener).blockDetected(any());
|
||||
when(socket.getInputStream()).then(invocation -> new ByteArrayInputStream("foo".getBytes()));
|
||||
|
||||
acceptAnother(50);
|
||||
waitUntilAllAccepted();
|
||||
|
||||
// Unblock all the daemon worker threads and shut down.
|
||||
latch.countDown();
|
||||
daemon.shutdown();
|
||||
|
||||
verify(blockListener, times(50)).blockDetected("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlockHashIsTrimmed() throws Exception {
|
||||
when(socket.getInputStream()).then(invocation -> new ByteArrayInputStream("\r\nbar \n".getBytes()));
|
||||
|
||||
acceptAnother(1);
|
||||
waitUntilAllAccepted();
|
||||
daemon.shutdown();
|
||||
|
||||
verify(blockListener).blockDetected("bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrokenSocketRead() throws Exception {
|
||||
when(socket.getInputStream()).thenThrow(IOException.class);
|
||||
|
||||
acceptAnother(1);
|
||||
errorHandlerLatch.await(5, TimeUnit.SECONDS);
|
||||
|
||||
verify(errorHandler).accept(argThat(t -> t instanceof NotificationHandlerException &&
|
||||
t.getCause() instanceof IOException));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuntimeExceptionInBlockListener() throws Exception {
|
||||
daemon.setBlockListener(blockHash -> {
|
||||
throw new IndexOutOfBoundsException();
|
||||
});
|
||||
when(socket.getInputStream()).then(invocation -> new ByteArrayInputStream("foo".getBytes()));
|
||||
|
||||
acceptAnother(1);
|
||||
errorHandlerLatch.await(5, TimeUnit.SECONDS);
|
||||
|
||||
verify(errorHandler).accept(argThat(t -> t instanceof NotificationHandlerException &&
|
||||
t.getCause() instanceof IndexOutOfBoundsException));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testErrorInBlockListener() throws Exception {
|
||||
synchronized (this) {
|
||||
daemon.setBlockListener(blockHash -> {
|
||||
throw new Error();
|
||||
});
|
||||
when(socket.getInputStream()).then(invocation -> new ByteArrayInputStream("foo".getBytes()));
|
||||
acceptAnother(1);
|
||||
}
|
||||
errorHandlerLatch.await(5, TimeUnit.SECONDS);
|
||||
|
||||
verify(errorHandler).accept(any(Error.class));
|
||||
}
|
||||
|
||||
@Test(expected = NotificationHandlerException.class)
|
||||
public void testUnknownHost() throws Exception {
|
||||
new BitcoindDaemon("[", -1, errorHandler).shutdown();
|
||||
}
|
||||
|
||||
private synchronized void acceptAnother(int n) {
|
||||
acceptAnotherCount += n;
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
private synchronized <V> V waitToAccept(Callable<V> onAccept) throws Exception {
|
||||
while (acceptAnotherCount == 0) {
|
||||
wait();
|
||||
}
|
||||
var result = onAccept.call();
|
||||
acceptAnotherCount--;
|
||||
notifyAll();
|
||||
return result;
|
||||
}
|
||||
|
||||
private synchronized void waitUntilAllAccepted() throws InterruptedException {
|
||||
while (acceptAnotherCount > 0) {
|
||||
wait();
|
||||
}
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
private interface ThrowableConsumer extends Consumer<Throwable> {
|
||||
}
|
||||
|
||||
private interface VoidAnswer extends Answer<Void> {
|
||||
void voidAnswer(InvocationOnMock invocation) throws Throwable;
|
||||
|
||||
@Override
|
||||
default Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
voidAnswer(invocation);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* 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.node.parser;
|
||||
|
||||
import bisq.core.dao.node.full.RawTx;
|
||||
import bisq.core.dao.node.full.RawTxOutput;
|
||||
import bisq.core.dao.node.parser.exceptions.InvalidGenesisTxException;
|
||||
import bisq.core.dao.state.model.blockchain.TxInput;
|
||||
import bisq.core.dao.state.model.blockchain.TxOutputType;
|
||||
import bisq.core.dao.state.model.blockchain.TxType;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class GenesisTxParserTest {
|
||||
|
||||
@Test
|
||||
public void testIsGenesis() {
|
||||
// fixme(chirhonul): Assert.assertEquals(2, 3);
|
||||
|
||||
int blockHeight = 200;
|
||||
String blockHash = "abc123";
|
||||
Coin genesisTotalSupply = Coin.parseCoin("2.5");
|
||||
long time = new Date().getTime();
|
||||
final List<TxInput> inputs = Arrays.asList(
|
||||
new TxInput("tx0", 0, null),
|
||||
new TxInput("tx1", 1, null)
|
||||
);
|
||||
RawTxOutput output = new RawTxOutput(
|
||||
0,
|
||||
genesisTotalSupply.value,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
blockHeight
|
||||
);
|
||||
RawTx rawTx = new RawTx(
|
||||
"tx2",
|
||||
blockHeight,
|
||||
blockHash,
|
||||
time,
|
||||
ImmutableList.copyOf(inputs),
|
||||
ImmutableList.copyOf(Arrays.asList(output))
|
||||
);
|
||||
|
||||
String genesisTxId = "genesisTxId";
|
||||
int genesisBlockHeight = 150;
|
||||
|
||||
// With mismatch in block height and tx id, we should not get genesis tx back.
|
||||
boolean result = GenesisTxParser.isGenesis(rawTx, genesisTxId, genesisBlockHeight);
|
||||
boolean want = false;
|
||||
Assert.assertEquals(want, result);
|
||||
|
||||
// With correct block height but mismatch in tx id, we should still not get genesis tx back.
|
||||
blockHeight = 150;
|
||||
rawTx = new RawTx(
|
||||
"tx2",
|
||||
blockHeight,
|
||||
blockHash,
|
||||
time,
|
||||
ImmutableList.copyOf(inputs),
|
||||
ImmutableList.copyOf(Arrays.asList(output))
|
||||
);
|
||||
result = GenesisTxParser.isGenesis(rawTx, genesisTxId, genesisBlockHeight);
|
||||
want = false;
|
||||
Assert.assertEquals(want, result);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGenesisTempTx() {
|
||||
int blockHeight = 200;
|
||||
String blockHash = "abc123";
|
||||
Coin genesisTotalSupply = Coin.parseCoin("2.5");
|
||||
long time = new Date().getTime();
|
||||
final List<TxInput> inputs = Arrays.asList(
|
||||
new TxInput("tx0", 0, null),
|
||||
new TxInput("tx1", 1, null)
|
||||
);
|
||||
RawTxOutput output = new RawTxOutput(
|
||||
0,
|
||||
genesisTotalSupply.value,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
blockHeight
|
||||
);
|
||||
|
||||
String genesisTxId = "genesisTxId";
|
||||
// With correct tx id and block height, we should find our genesis tx with correct tx and output type.
|
||||
RawTx rawTx = new RawTx(
|
||||
genesisTxId,
|
||||
blockHeight,
|
||||
blockHash,
|
||||
time,
|
||||
ImmutableList.copyOf(inputs),
|
||||
ImmutableList.copyOf(Arrays.asList(output))
|
||||
);
|
||||
TempTx resultTempTx = GenesisTxParser.getGenesisTempTx(rawTx, genesisTotalSupply);
|
||||
|
||||
TempTx tempTx = TempTx.fromRawTx(rawTx);
|
||||
tempTx.setTxType(TxType.GENESIS);
|
||||
for (int i = 0; i < tempTx.getTempTxOutputs().size(); ++i) {
|
||||
tempTx.getTempTxOutputs().get(i).setTxOutputType(TxOutputType.GENESIS_OUTPUT);
|
||||
}
|
||||
TempTx wantTempTx = tempTx;
|
||||
|
||||
Assert.assertEquals(wantTempTx, resultTempTx);
|
||||
|
||||
// With correct tx id and block height, but too low sum of outputs (lower than genesisTotalSupply), we
|
||||
// should see an exception raised.
|
||||
output = new RawTxOutput(
|
||||
0,
|
||||
genesisTotalSupply.value - 1,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
blockHeight
|
||||
);
|
||||
rawTx = new RawTx(
|
||||
genesisTxId,
|
||||
blockHeight,
|
||||
blockHash,
|
||||
time,
|
||||
ImmutableList.copyOf(inputs),
|
||||
ImmutableList.copyOf(Arrays.asList(output))
|
||||
);
|
||||
try {
|
||||
GenesisTxParser.getGenesisTempTx(rawTx, genesisTotalSupply);
|
||||
Assert.fail("Expected an InvalidGenesisTxException to be thrown when outputs are too low");
|
||||
} catch (InvalidGenesisTxException igtxe) {
|
||||
String wantMessage = "Genesis tx is invalid; not using all available inputs. Remaining input value is 1 sat";
|
||||
Assert.assertTrue("Unexpected exception, want message starting with " +
|
||||
"'" + wantMessage + "', got '" + igtxe.getMessage() + "'", igtxe.getMessage().startsWith(wantMessage));
|
||||
}
|
||||
|
||||
// With correct tx id and block height, but too high sum of outputs (higher than from genesisTotalSupply), we
|
||||
// should see an exception raised.
|
||||
RawTxOutput output1 = new RawTxOutput(
|
||||
0,
|
||||
genesisTotalSupply.value - 2,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
blockHeight
|
||||
);
|
||||
RawTxOutput output2 = new RawTxOutput(
|
||||
0,
|
||||
3,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
blockHeight
|
||||
);
|
||||
rawTx = new RawTx(
|
||||
genesisTxId,
|
||||
blockHeight,
|
||||
blockHash,
|
||||
time,
|
||||
ImmutableList.copyOf(inputs),
|
||||
ImmutableList.copyOf(Arrays.asList(output1, output2))
|
||||
);
|
||||
try {
|
||||
GenesisTxParser.getGenesisTempTx(rawTx, genesisTotalSupply);
|
||||
Assert.fail("Expected an InvalidGenesisTxException to be thrown when outputs are too high");
|
||||
} catch (InvalidGenesisTxException igtxe) {
|
||||
String wantMessage = "Genesis tx is invalid; using more than available inputs. Remaining input value is 2 sat";
|
||||
Assert.assertTrue("Unexpected exception, want message starting with " +
|
||||
"'" + wantMessage + "', got '" + igtxe.getMessage() + "'", igtxe.getMessage().startsWith(wantMessage));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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.state;
|
||||
|
||||
import bisq.core.dao.state.model.DaoState;
|
||||
import bisq.core.dao.state.model.blockchain.Block;
|
||||
import bisq.core.util.coin.BsqFormatter;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DaoStateServiceTest {
|
||||
@Test
|
||||
public void testIsBlockHashKnown() {
|
||||
DaoStateService stateService = new DaoStateService(
|
||||
new DaoState(),
|
||||
new GenesisTxInfo("fakegenesistxid", 100, Coin.parseCoin("2.5").value),
|
||||
new BsqFormatter());
|
||||
Assert.assertEquals(
|
||||
"Unknown block should not exist.",
|
||||
false,
|
||||
stateService.isBlockHashKnown("fakeblockhash0")
|
||||
);
|
||||
|
||||
Block block = new Block(0, 1534800000, "fakeblockhash0", null);
|
||||
stateService.onNewBlockHeight(0);
|
||||
stateService.onNewBlockWithEmptyTxs(block);
|
||||
Assert.assertEquals(
|
||||
"Block has to be genesis block to get added.",
|
||||
false,
|
||||
stateService.isBlockHashKnown("fakeblockhash0")
|
||||
);
|
||||
|
||||
Assert.assertEquals(
|
||||
"Block that was never added should still not exist.",
|
||||
false,
|
||||
stateService.isBlockHashKnown("fakeblockhash1")
|
||||
);
|
||||
|
||||
block = new Block(1, 1534800001, "fakeblockhash1", null);
|
||||
stateService.onNewBlockHeight(1);
|
||||
stateService.onNewBlockWithEmptyTxs(block);
|
||||
block = new Block(2, 1534800002, "fakeblockhash2", null);
|
||||
stateService.onNewBlockHeight(2);
|
||||
stateService.onNewBlockWithEmptyTxs(block);
|
||||
block = new Block(3, 1534800003, "fakeblockhash3", null);
|
||||
stateService.onNewBlockHeight(3);
|
||||
stateService.onNewBlockWithEmptyTxs(block);
|
||||
Assert.assertEquals(
|
||||
"Block that was never added should still not exist after adding more blocks.",
|
||||
false,
|
||||
stateService.isBlockHashKnown("fakeblockhash4")
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.state;
|
||||
|
||||
import bisq.core.dao.monitoring.DaoStateMonitoringService;
|
||||
import bisq.core.dao.state.storage.DaoStateStorageService;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class DaoStateSnapshotServiceTest {
|
||||
|
||||
private DaoStateSnapshotService daoStateSnapshotService;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
daoStateSnapshotService = new DaoStateSnapshotService(mock(DaoStateService.class),
|
||||
mock(GenesisTxInfo.class),
|
||||
mock(DaoStateStorageService.class),
|
||||
mock(DaoStateMonitoringService.class),
|
||||
null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSnapshotHeight() {
|
||||
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 0, 10));
|
||||
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 100, 10));
|
||||
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 102, 10));
|
||||
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 119, 10));
|
||||
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 120, 10));
|
||||
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 121, 10));
|
||||
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 130, 10));
|
||||
assertEquals(120, daoStateSnapshotService.getSnapshotHeight(102, 139, 10));
|
||||
assertEquals(130, daoStateSnapshotService.getSnapshotHeight(102, 140, 10));
|
||||
assertEquals(130, daoStateSnapshotService.getSnapshotHeight(102, 141, 10));
|
||||
assertEquals(990, daoStateSnapshotService.getSnapshotHeight(102, 1000, 10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnapshotHeight() {
|
||||
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 0, 10));
|
||||
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 80, 10));
|
||||
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 90, 10));
|
||||
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 100, 10));
|
||||
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 119, 10));
|
||||
assertTrue(daoStateSnapshotService.isSnapshotHeight(102, 120, 10));
|
||||
assertTrue(daoStateSnapshotService.isSnapshotHeight(102, 130, 10));
|
||||
assertTrue(daoStateSnapshotService.isSnapshotHeight(102, 140, 10));
|
||||
assertTrue(daoStateSnapshotService.isSnapshotHeight(102, 200, 10));
|
||||
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 201, 10));
|
||||
assertFalse(daoStateSnapshotService.isSnapshotHeight(102, 199, 10));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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.voting.voteresult;
|
||||
|
||||
import bisq.core.dao.governance.merit.MeritConsensus;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@Slf4j
|
||||
public class VoteResultConsensusTest {
|
||||
@Before
|
||||
public void setup() {
|
||||
}
|
||||
|
||||
@Rule
|
||||
public final ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testGetWeightedMeritAmount() {
|
||||
int currentChainHeight;
|
||||
int blocksPerYear = 50_000; // 144*365=51264;
|
||||
currentChainHeight = 1_000_000;
|
||||
|
||||
assertEquals("fresh issuance", 100000, MeritConsensus.getWeightedMeritAmount(100_000, 1_000_000,
|
||||
currentChainHeight, blocksPerYear));
|
||||
assertEquals("0.5 year old issuance", 75000, MeritConsensus.getWeightedMeritAmount(100_000, 975_000,
|
||||
currentChainHeight, blocksPerYear));
|
||||
assertEquals("1 year old issuance", 50000, MeritConsensus.getWeightedMeritAmount(100_000, 950_000,
|
||||
currentChainHeight, blocksPerYear));
|
||||
assertEquals("1.5 year old issuance", 25000, MeritConsensus.getWeightedMeritAmount(100_000, 925_000,
|
||||
currentChainHeight, blocksPerYear));
|
||||
assertEquals("2 year old issuance", 0, MeritConsensus.getWeightedMeritAmount(100_000, 900_000,
|
||||
currentChainHeight, blocksPerYear));
|
||||
assertEquals("3 year old issuance", 0, MeritConsensus.getWeightedMeritAmount(100_000, 850_000,
|
||||
currentChainHeight, blocksPerYear));
|
||||
|
||||
|
||||
assertEquals("1 block old issuance", 99999, MeritConsensus.getWeightedMeritAmount(100_000, 999_999,
|
||||
currentChainHeight, blocksPerYear));
|
||||
assertEquals("2 block old issuance", 99998, MeritConsensus.getWeightedMeritAmount(100_000, 999_998,
|
||||
currentChainHeight, blocksPerYear));
|
||||
assertEquals("10 blocks old issuance", 99990, MeritConsensus.getWeightedMeritAmount(100_000, 999_990,
|
||||
currentChainHeight, blocksPerYear));
|
||||
assertEquals("100 blocks old issuance", 99900, MeritConsensus.getWeightedMeritAmount(100_000, 999_900,
|
||||
currentChainHeight, blocksPerYear));
|
||||
assertEquals("99_999 blocks old issuance", 1, MeritConsensus.getWeightedMeritAmount(100_000, 900_001,
|
||||
currentChainHeight, blocksPerYear));
|
||||
assertEquals("99_990 blocks old issuance", 10, MeritConsensus.getWeightedMeritAmount(100_000, 900_010,
|
||||
currentChainHeight, blocksPerYear));
|
||||
assertEquals("100_001 blocks old issuance", 0, MeritConsensus.getWeightedMeritAmount(100_000, 899_999,
|
||||
currentChainHeight, blocksPerYear));
|
||||
assertEquals("1_000_000 blocks old issuance", 0, MeritConsensus.getWeightedMeritAmount(100_000, 0,
|
||||
currentChainHeight, blocksPerYear));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidChainHeight() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
MeritConsensus.getWeightedMeritAmount(100_000, 2_000_000, 1_000_000, 1_000_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidIssuanceHeight() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
MeritConsensus.getWeightedMeritAmount(100_000, -1, 1_000_000, 1_000_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidAmount() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
MeritConsensus.getWeightedMeritAmount(-100_000, 1, 1_000_000, 1_000_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidCurrentChainHeight() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
MeritConsensus.getWeightedMeritAmount(100_000, 1, -1, 1_000_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidBlockPerYear() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
MeritConsensus.getWeightedMeritAmount(100_000, 1, 11, -1);
|
||||
}
|
||||
}
|
47
core/src/test/java/bisq/core/locale/BankUtilTest.java
Normal file
47
core/src/test/java/bisq/core/locale/BankUtilTest.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
package bisq.core.locale;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class BankUtilTest {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Locale.setDefault(new Locale("en", "US"));
|
||||
GlobalSettings.setLocale(new Locale("en", "US"));
|
||||
Res.setBaseCurrencyCode("BTC");
|
||||
Res.setBaseCurrencyName("Bitcoin");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBankFieldsForArgentina() {
|
||||
final String argentina = "AR";
|
||||
|
||||
assertTrue(BankUtil.isHolderIdRequired(argentina));
|
||||
assertEquals("CUIL/CUIT", BankUtil.getHolderIdLabel(argentina));
|
||||
assertEquals("CUIT", BankUtil.getHolderIdLabelShort(argentina));
|
||||
|
||||
assertTrue(BankUtil.isNationalAccountIdRequired(argentina));
|
||||
assertEquals("CBU number", BankUtil.getNationalAccountIdLabel(argentina));
|
||||
|
||||
assertTrue(BankUtil.isBankNameRequired(argentina));
|
||||
|
||||
assertTrue(BankUtil.isBranchIdRequired(argentina));
|
||||
assertTrue(BankUtil.isAccountNrRequired(argentina));
|
||||
assertEquals("Número de cuenta", BankUtil.getAccountNrLabel(argentina));
|
||||
|
||||
assertTrue(BankUtil.useValidation(argentina));
|
||||
|
||||
assertFalse(BankUtil.isBankIdRequired(argentina));
|
||||
assertFalse(BankUtil.isStateRequired(argentina));
|
||||
assertFalse(BankUtil.isAccountTypeRequired(argentina));
|
||||
|
||||
}
|
||||
|
||||
}
|
155
core/src/test/java/bisq/core/locale/CurrencyUtilTest.java
Normal file
155
core/src/test/java/bisq/core/locale/CurrencyUtilTest.java
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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.locale;
|
||||
|
||||
import bisq.common.config.BaseCurrencyNetwork;
|
||||
|
||||
import bisq.asset.Asset;
|
||||
import bisq.asset.AssetRegistry;
|
||||
import bisq.asset.Coin;
|
||||
import bisq.asset.coins.Ether;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CurrencyUtilTest {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
Locale.setDefault(new Locale("en", "US"));
|
||||
Res.setBaseCurrencyCode("BTC");
|
||||
Res.setBaseCurrencyName("Bitcoin");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTradeCurrency() {
|
||||
Optional<TradeCurrency> euro = CurrencyUtil.getTradeCurrency("EUR");
|
||||
Optional<TradeCurrency> naira = CurrencyUtil.getTradeCurrency("NGN");
|
||||
Optional<TradeCurrency> fake = CurrencyUtil.getTradeCurrency("FAK");
|
||||
|
||||
assertTrue(euro.isPresent());
|
||||
assertTrue(naira.isPresent());
|
||||
assertFalse("Fake currency shouldn't exist", fake.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindAsset() {
|
||||
MockAssetRegistry assetRegistry = new MockAssetRegistry();
|
||||
|
||||
// test if code is matching
|
||||
boolean daoTradingActivated = false;
|
||||
// Test if BSQ on mainnet is failing
|
||||
Assert.assertFalse(CurrencyUtil.findAsset(assetRegistry, "BSQ",
|
||||
BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).isPresent());
|
||||
|
||||
// on testnet/regtest it is allowed
|
||||
assertEquals(CurrencyUtil.findAsset(assetRegistry, "BSQ",
|
||||
BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get().getTickerSymbol(), "BSQ");
|
||||
|
||||
|
||||
daoTradingActivated = true;
|
||||
// With daoTradingActivated we can request BSQ
|
||||
assertEquals(CurrencyUtil.findAsset(assetRegistry, "BSQ",
|
||||
BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).get().getTickerSymbol(), "BSQ");
|
||||
|
||||
// Test if not matching ticker is failing
|
||||
Assert.assertFalse(CurrencyUtil.findAsset(assetRegistry, "BSQ1",
|
||||
BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).isPresent());
|
||||
|
||||
// Add a mock coin which has no mainnet version, needs to fail if we are on mainnet
|
||||
MockTestnetCoin.Testnet mockTestnetCoin = new MockTestnetCoin.Testnet();
|
||||
try {
|
||||
assetRegistry.addAsset(mockTestnetCoin);
|
||||
CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN",
|
||||
BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated);
|
||||
Assert.fail("Expected an IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
String wantMessage = "We are on mainnet and we could not find an asset with network type mainnet";
|
||||
Assert.assertTrue("Unexpected exception, want message starting with " +
|
||||
"'" + wantMessage + "', got '" + e.getMessage() + "'", e.getMessage().startsWith(wantMessage));
|
||||
}
|
||||
|
||||
// For testnet its ok
|
||||
assertEquals(CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN",
|
||||
BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get().getTickerSymbol(), "MOCK_COIN");
|
||||
assertEquals(Coin.Network.TESTNET, mockTestnetCoin.getNetwork());
|
||||
|
||||
// For regtest its still found
|
||||
assertEquals(CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN",
|
||||
BaseCurrencyNetwork.BTC_REGTEST, daoTradingActivated).get().getTickerSymbol(), "MOCK_COIN");
|
||||
|
||||
|
||||
// We test if we are not on mainnet to get the mainnet coin
|
||||
Coin ether = new Ether();
|
||||
assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH",
|
||||
BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get().getTickerSymbol(), "ETH");
|
||||
assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH",
|
||||
BaseCurrencyNetwork.BTC_REGTEST, daoTradingActivated).get().getTickerSymbol(), "ETH");
|
||||
assertEquals(Coin.Network.MAINNET, ether.getNetwork());
|
||||
|
||||
// We test if network matches exactly if there are distinct network types defined like with BSQ
|
||||
Coin bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).get();
|
||||
assertEquals("BSQ", bsq.getTickerSymbol());
|
||||
assertEquals(Coin.Network.MAINNET, bsq.getNetwork());
|
||||
|
||||
bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get();
|
||||
assertEquals("BSQ", bsq.getTickerSymbol());
|
||||
assertEquals(Coin.Network.TESTNET, bsq.getNetwork());
|
||||
|
||||
bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.BTC_REGTEST, daoTradingActivated).get();
|
||||
assertEquals("BSQ", bsq.getTickerSymbol());
|
||||
assertEquals(Coin.Network.REGTEST, bsq.getNetwork());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNameAndCodeOfRemovedAsset() {
|
||||
assertEquals("Bitcoin Cash (BCH)", CurrencyUtil.getNameAndCode("BCH"));
|
||||
assertEquals("N/A (XYZ)", CurrencyUtil.getNameAndCode("XYZ"));
|
||||
}
|
||||
|
||||
class MockAssetRegistry extends AssetRegistry {
|
||||
private List<Asset> registeredAssets = new ArrayList<>();
|
||||
|
||||
MockAssetRegistry() {
|
||||
for (Asset asset : ServiceLoader.load(Asset.class)) {
|
||||
registeredAssets.add(asset);
|
||||
}
|
||||
}
|
||||
|
||||
void addAsset(Asset asset) {
|
||||
registeredAssets.add(asset);
|
||||
}
|
||||
|
||||
public Stream<Asset> stream() {
|
||||
return registeredAssets.stream();
|
||||
}
|
||||
}
|
||||
}
|
69
core/src/test/java/bisq/core/locale/MockTestnetCoin.java
Normal file
69
core/src/test/java/bisq/core/locale/MockTestnetCoin.java
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.locale;
|
||||
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.params.MainNetParams;
|
||||
import org.bitcoinj.params.RegTestParams;
|
||||
import org.bitcoinj.params.TestNet3Params;
|
||||
|
||||
|
||||
|
||||
import bisq.asset.AddressValidationResult;
|
||||
import bisq.asset.Base58AddressValidator;
|
||||
import bisq.asset.Coin;
|
||||
|
||||
public class MockTestnetCoin extends Coin {
|
||||
|
||||
public MockTestnetCoin(Network network, NetworkParameters networkParameters) {
|
||||
super("MockTestnetCoin", "MOCK_COIN", new BSQAddressValidator(networkParameters), network);
|
||||
}
|
||||
|
||||
public static class Mainnet extends MockTestnetCoin {
|
||||
|
||||
public Mainnet() {
|
||||
super(Network.MAINNET, MainNetParams.get());
|
||||
}
|
||||
}
|
||||
|
||||
public static class Testnet extends MockTestnetCoin {
|
||||
|
||||
public Testnet() {
|
||||
super(Network.TESTNET, TestNet3Params.get());
|
||||
}
|
||||
}
|
||||
|
||||
public static class Regtest extends MockTestnetCoin {
|
||||
|
||||
public Regtest() {
|
||||
super(Network.REGTEST, RegTestParams.get());
|
||||
}
|
||||
}
|
||||
|
||||
public static class BSQAddressValidator extends Base58AddressValidator {
|
||||
|
||||
public BSQAddressValidator(NetworkParameters networkParameters) {
|
||||
super(networkParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressValidationResult validate(String address) {
|
||||
return super.validate(address);
|
||||
}
|
||||
}
|
||||
}
|
69
core/src/test/java/bisq/core/message/MarshallerTest.java
Normal file
69
core/src/test/java/bisq/core/message/MarshallerTest.java
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.message;
|
||||
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@Slf4j
|
||||
public class MarshallerTest {
|
||||
|
||||
@Test
|
||||
public void getBaseEnvelopeTest() {
|
||||
protobuf.Ping Ping = protobuf.Ping.newBuilder().setNonce(100).build();
|
||||
protobuf.Pong Pong = protobuf.Pong.newBuilder().setRequestNonce(1000).build();
|
||||
protobuf.NetworkEnvelope envelope1 = protobuf.NetworkEnvelope.newBuilder().setPing(Ping).build();
|
||||
protobuf.NetworkEnvelope envelope2 = protobuf.NetworkEnvelope.newBuilder().setPong(Pong).build();
|
||||
log.info(Ping.toString());
|
||||
log.info(Pong.toString());
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try {
|
||||
envelope1.writeDelimitedTo(outputStream);
|
||||
envelope2.writeDelimitedTo(outputStream);
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||
protobuf.NetworkEnvelope envelope3 = protobuf.NetworkEnvelope.parseDelimitedFrom(inputStream);
|
||||
protobuf.NetworkEnvelope envelope4 = protobuf.NetworkEnvelope.parseDelimitedFrom(inputStream);
|
||||
|
||||
|
||||
log.info("message: {}", envelope3.getPing());
|
||||
//log.info("peerseesd empty: '{}'",envelope3.getPong().equals(PB.NetworkEnvelope.) == "");
|
||||
assertTrue(isPing(envelope3));
|
||||
assertTrue(!isPing(envelope4));
|
||||
|
||||
log.info("3 = {} 4 = {}", isPing(envelope3), isPing(envelope4));
|
||||
log.info(envelope3.toString());
|
||||
log.info(envelope4.toString());
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isPing(protobuf.NetworkEnvelope envelope) {
|
||||
return !envelope.getPing().getDefaultInstanceForType().equals(envelope.getPing());
|
||||
}
|
||||
}
|
133
core/src/test/java/bisq/core/monetary/PriceTest.java
Normal file
133
core/src/test/java/bisq/core/monetary/PriceTest.java
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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.monetary;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PriceTest {
|
||||
|
||||
@Test
|
||||
public void testParse() {
|
||||
Price result = Price.parse("USD", "0.1");
|
||||
Assert.assertEquals(
|
||||
"Fiat value should be formatted with two decimals.",
|
||||
"0.10 BTC/USD",
|
||||
result.toFriendlyString()
|
||||
);
|
||||
|
||||
result = Price.parse("EUR", "0.1234");
|
||||
Assert.assertEquals(
|
||||
"Fiat value should be given two decimals",
|
||||
"0.1234 BTC/EUR",
|
||||
result.toFriendlyString()
|
||||
);
|
||||
|
||||
try {
|
||||
Price.parse("EUR", "0.12345");
|
||||
Assert.fail("Expected IllegalArgumentException to be thrown when too many decimals are used.");
|
||||
} catch (IllegalArgumentException iae) {
|
||||
Assert.assertEquals(
|
||||
"Unexpected exception message.",
|
||||
"java.lang.ArithmeticException: Rounding necessary",
|
||||
iae.getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
Assert.assertEquals(
|
||||
"Negative value should be parsed correctly.",
|
||||
-100000000L,
|
||||
Price.parse("LTC", "-1").getValue()
|
||||
);
|
||||
|
||||
Assert.assertEquals(
|
||||
"Comma (',') as decimal separator should be converted to period ('.')",
|
||||
"0.0001 BTC/USD",
|
||||
Price.parse("USD", "0,0001").toFriendlyString()
|
||||
);
|
||||
|
||||
Assert.assertEquals(
|
||||
"Too many decimals should get rounded up properly.",
|
||||
"10000.2346 LTC/BTC",
|
||||
Price.parse("LTC", "10000,23456789").toFriendlyString()
|
||||
);
|
||||
|
||||
Assert.assertEquals(
|
||||
"Too many decimals should get rounded down properly.",
|
||||
"10000.2345 LTC/BTC",
|
||||
Price.parse("LTC", "10000,23454999").toFriendlyString()
|
||||
);
|
||||
|
||||
Assert.assertEquals(
|
||||
"Underlying long value should be correct.",
|
||||
1000023456789L,
|
||||
Price.parse("LTC", "10000,23456789").getValue()
|
||||
);
|
||||
|
||||
try {
|
||||
Price.parse("XMR", "56789.123456789");
|
||||
Assert.fail("Expected IllegalArgumentException to be thrown when too many decimals are used.");
|
||||
} catch (IllegalArgumentException iae) {
|
||||
Assert.assertEquals(
|
||||
"Unexpected exception message.",
|
||||
"java.lang.ArithmeticException: Rounding necessary",
|
||||
iae.getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void testValueOf() {
|
||||
Price result = Price.valueOf("USD", 1);
|
||||
Assert.assertEquals(
|
||||
"Fiat value should have four decimals.",
|
||||
"0.0001 BTC/USD",
|
||||
result.toFriendlyString()
|
||||
);
|
||||
|
||||
result = Price.valueOf("EUR", 1234);
|
||||
Assert.assertEquals(
|
||||
"Fiat value should be given two decimals",
|
||||
"0.1234 BTC/EUR",
|
||||
result.toFriendlyString()
|
||||
);
|
||||
|
||||
Assert.assertEquals(
|
||||
"Negative value should be parsed correctly.",
|
||||
-1L,
|
||||
Price.valueOf("LTC", -1L).getValue()
|
||||
);
|
||||
|
||||
Assert.assertEquals(
|
||||
"Too many decimals should get rounded up properly.",
|
||||
"10000.2346 LTC/BTC",
|
||||
Price.valueOf("LTC", 1000023456789L).toFriendlyString()
|
||||
);
|
||||
|
||||
Assert.assertEquals(
|
||||
"Too many decimals should get rounded down properly.",
|
||||
"10000.2345 LTC/BTC",
|
||||
Price.valueOf("LTC", 1000023454999L).toFriendlyString()
|
||||
);
|
||||
|
||||
Assert.assertEquals(
|
||||
"Underlying long value should be correct.",
|
||||
1000023456789L,
|
||||
Price.valueOf("LTC", 1000023456789L).getValue()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.network.p2p.seed;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
public class DefaultSeedNodeRepositoryTest {
|
||||
|
||||
@Test
|
||||
public void getSeedNodes() {
|
||||
DefaultSeedNodeRepository DUT = new DefaultSeedNodeRepository(new Config());
|
||||
Assert.assertFalse(DUT.getSeedNodeAddresses().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void manualSeedNodes() {
|
||||
String seed1 = "asdf:8001";
|
||||
String seed2 = "fdsa:6001";
|
||||
String seedNodesOption = format("--%s=%s,%s", Config.SEED_NODES, seed1, seed2);
|
||||
DefaultSeedNodeRepository DUT = new DefaultSeedNodeRepository(new Config(seedNodesOption));
|
||||
Assert.assertFalse(DUT.getSeedNodeAddresses().isEmpty());
|
||||
Assert.assertEquals(2, DUT.getSeedNodeAddresses().size());
|
||||
Assert.assertTrue(DUT.getSeedNodeAddresses().contains(new NodeAddress(seed1)));
|
||||
Assert.assertTrue(DUT.getSeedNodeAddresses().contains(new NodeAddress(seed2)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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.notifications;
|
||||
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@Slf4j
|
||||
public class MobileModelTest {
|
||||
|
||||
@Test
|
||||
public void testParseDescriptor() {
|
||||
MobileModel mobileModel = new MobileModel();
|
||||
List<Tuple2<String, Boolean>> list = Arrays.asList(
|
||||
new Tuple2<>("iPod Touch 5", false),
|
||||
new Tuple2<>("iPod Touch 6", false),
|
||||
new Tuple2<>("iPhone 4", false),
|
||||
new Tuple2<>("iPhone 4s", false),
|
||||
new Tuple2<>("iPhone 5", false),
|
||||
new Tuple2<>("iPhone 5c", false),
|
||||
new Tuple2<>("iPhone 5s", false),
|
||||
|
||||
new Tuple2<>("iPhone 6", false),
|
||||
new Tuple2<>("iPhone 6 Plus", false),
|
||||
new Tuple2<>("iPhone 6s", true),
|
||||
new Tuple2<>("iPhone 6s Plus", true),
|
||||
|
||||
new Tuple2<>("iPhone 7", true),
|
||||
new Tuple2<>("iPhone 7 Plus", true),
|
||||
new Tuple2<>("iPhone SE", false), // unclear
|
||||
new Tuple2<>("iPhone 8", true),
|
||||
new Tuple2<>("iPhone 8 Plus", true),
|
||||
new Tuple2<>("iPhone X", true),
|
||||
new Tuple2<>("iPhone XS", true),
|
||||
new Tuple2<>("iPhone XS Max", true),
|
||||
new Tuple2<>("iPhone XR", true),
|
||||
new Tuple2<>("iPhone 11", true),
|
||||
new Tuple2<>("iPhone 11 Pro", true),
|
||||
new Tuple2<>("iPhone 11 Pro Max", true),
|
||||
new Tuple2<>("iPhone 11S", true), // not sure if this model will exist, but based on past versioning it is possible
|
||||
// need to ensure it will be parsed correctly just in case
|
||||
|
||||
new Tuple2<>("iPad 2", false),
|
||||
new Tuple2<>("iPad 3", false),
|
||||
new Tuple2<>("iPad 4", false),
|
||||
new Tuple2<>("iPad Air", false),
|
||||
new Tuple2<>("iPad Air 2", false),
|
||||
new Tuple2<>("iPad 5", false),
|
||||
new Tuple2<>("iPad 6", false),
|
||||
new Tuple2<>("iPad Mini", false),
|
||||
new Tuple2<>("iPad Mini 2", false),
|
||||
new Tuple2<>("iPad Mini 3", false),
|
||||
new Tuple2<>("iPad Mini 4", false),
|
||||
|
||||
new Tuple2<>("iPad Pro 9.7 Inch", true),
|
||||
new Tuple2<>("iPad Pro 12.9 Inch", true),
|
||||
new Tuple2<>("iPad Pro 12.9 Inch 2. Generation", true),
|
||||
new Tuple2<>("iPad Pro 10.5 Inch", true)
|
||||
);
|
||||
|
||||
list.forEach(tuple -> {
|
||||
log.info(tuple.toString());
|
||||
assertEquals("tuple: " + tuple, mobileModel.parseDescriptor(tuple.first), tuple.second);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
79
core/src/test/java/bisq/core/offer/OfferMaker.java
Normal file
79
core/src/test/java/bisq/core/offer/OfferMaker.java
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.offer;
|
||||
|
||||
import com.natpryce.makeiteasy.Instantiator;
|
||||
import com.natpryce.makeiteasy.Maker;
|
||||
import com.natpryce.makeiteasy.Property;
|
||||
|
||||
import static com.natpryce.makeiteasy.MakeItEasy.a;
|
||||
|
||||
public class OfferMaker {
|
||||
|
||||
public static final Property<Offer, Long> price = new Property<>();
|
||||
public static final Property<Offer, Long> minAmount = new Property<>();
|
||||
public static final Property<Offer, Long> amount = new Property<>();
|
||||
public static final Property<Offer, String> baseCurrencyCode = new Property<>();
|
||||
public static final Property<Offer, String> counterCurrencyCode = new Property<>();
|
||||
public static final Property<Offer, OfferPayload.Direction> direction = new Property<>();
|
||||
public static final Property<Offer, Boolean> useMarketBasedPrice = new Property<>();
|
||||
public static final Property<Offer, Double> marketPriceMargin = new Property<>();
|
||||
public static final Property<Offer, String> id = new Property<>();
|
||||
|
||||
public static final Instantiator<Offer> Offer = lookup -> new Offer(
|
||||
new OfferPayload(lookup.valueOf(id, "1234"),
|
||||
0L,
|
||||
null,
|
||||
null,
|
||||
lookup.valueOf(direction, OfferPayload.Direction.BUY),
|
||||
lookup.valueOf(price, 100000L),
|
||||
lookup.valueOf(marketPriceMargin, 0.0),
|
||||
lookup.valueOf(useMarketBasedPrice, false),
|
||||
lookup.valueOf(amount, 100000L),
|
||||
lookup.valueOf(minAmount, 100000L),
|
||||
lookup.valueOf(baseCurrencyCode, "BTC"),
|
||||
lookup.valueOf(counterCurrencyCode, "USD"),
|
||||
null,
|
||||
null,
|
||||
"SEPA",
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"",
|
||||
0L,
|
||||
0L,
|
||||
0L,
|
||||
false,
|
||||
0L,
|
||||
0L,
|
||||
0L,
|
||||
0L,
|
||||
false,
|
||||
false,
|
||||
0L,
|
||||
0L,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
0));
|
||||
|
||||
public static final Maker<Offer> btcUsdOffer = a(Offer);
|
||||
}
|
48
core/src/test/java/bisq/core/offer/OfferTest.java
Normal file
48
core/src/test/java/bisq/core/offer/OfferTest.java
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.offer;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class OfferTest {
|
||||
|
||||
@Test
|
||||
public void testHasNoRange() {
|
||||
OfferPayload payload = mock(OfferPayload.class);
|
||||
when(payload.getMinAmount()).thenReturn(1000L);
|
||||
when(payload.getAmount()).thenReturn(1000L);
|
||||
|
||||
Offer offer = new Offer(payload);
|
||||
assertFalse(offer.isRange());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasRange() {
|
||||
OfferPayload payload = mock(OfferPayload.class);
|
||||
when(payload.getMinAmount()).thenReturn(1000L);
|
||||
when(payload.getAmount()).thenReturn(2000L);
|
||||
|
||||
Offer offer = new Offer(payload);
|
||||
assertTrue(offer.isRange());
|
||||
}
|
||||
}
|
176
core/src/test/java/bisq/core/offer/OpenOfferManagerTest.java
Normal file
176
core/src/test/java/bisq/core/offer/OpenOfferManagerTest.java
Normal file
|
@ -0,0 +1,176 @@
|
|||
package bisq.core.offer;
|
||||
|
||||
import bisq.core.api.CoreContext;
|
||||
import bisq.core.trade.TradableList;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
import bisq.network.p2p.peers.PeerManager;
|
||||
|
||||
import bisq.common.file.CorruptedStorageFileHandler;
|
||||
import bisq.common.handlers.ErrorMessageHandler;
|
||||
import bisq.common.handlers.ResultHandler;
|
||||
import bisq.common.persistence.PersistenceManager;
|
||||
|
||||
import java.nio.file.Files;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static bisq.core.offer.OfferMaker.btcUsdOffer;
|
||||
import static com.natpryce.makeiteasy.MakeItEasy.make;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class OpenOfferManagerTest {
|
||||
private PersistenceManager<TradableList<OpenOffer>> persistenceManager;
|
||||
private CoreContext coreContext;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
var corruptedStorageFileHandler = mock(CorruptedStorageFileHandler.class);
|
||||
var storageDir = Files.createTempDirectory("storage").toFile();
|
||||
persistenceManager = new PersistenceManager<>(storageDir, null, corruptedStorageFileHandler);
|
||||
coreContext = new CoreContext();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
persistenceManager.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartEditOfferForActiveOffer() {
|
||||
P2PService p2PService = mock(P2PService.class);
|
||||
OfferBookService offerBookService = mock(OfferBookService.class);
|
||||
|
||||
when(p2PService.getPeerManager()).thenReturn(mock(PeerManager.class));
|
||||
|
||||
final OpenOfferManager manager = new OpenOfferManager(coreContext,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
p2PService,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
offerBookService,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
persistenceManager);
|
||||
|
||||
AtomicBoolean startEditOfferSuccessful = new AtomicBoolean(false);
|
||||
|
||||
|
||||
doAnswer(invocation -> {
|
||||
((ResultHandler) invocation.getArgument(1)).handleResult();
|
||||
return null;
|
||||
}).when(offerBookService).deactivateOffer(any(OfferPayload.class), any(ResultHandler.class), any(ErrorMessageHandler.class));
|
||||
|
||||
final OpenOffer openOffer = new OpenOffer(make(btcUsdOffer));
|
||||
|
||||
ResultHandler resultHandler = () -> startEditOfferSuccessful.set(true);
|
||||
|
||||
manager.editOpenOfferStart(openOffer, resultHandler, null);
|
||||
|
||||
verify(offerBookService, times(1)).deactivateOffer(any(OfferPayload.class), any(ResultHandler.class), any(ErrorMessageHandler.class));
|
||||
|
||||
assertTrue(startEditOfferSuccessful.get());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartEditOfferForDeactivatedOffer() {
|
||||
P2PService p2PService = mock(P2PService.class);
|
||||
OfferBookService offerBookService = mock(OfferBookService.class);
|
||||
when(p2PService.getPeerManager()).thenReturn(mock(PeerManager.class));
|
||||
|
||||
final OpenOfferManager manager = new OpenOfferManager(coreContext,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
p2PService,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
offerBookService,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
persistenceManager);
|
||||
|
||||
AtomicBoolean startEditOfferSuccessful = new AtomicBoolean(false);
|
||||
|
||||
ResultHandler resultHandler = () -> startEditOfferSuccessful.set(true);
|
||||
|
||||
final OpenOffer openOffer = new OpenOffer(make(btcUsdOffer));
|
||||
openOffer.setState(OpenOffer.State.DEACTIVATED);
|
||||
|
||||
manager.editOpenOfferStart(openOffer, resultHandler, null);
|
||||
assertTrue(startEditOfferSuccessful.get());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartEditOfferForOfferThatIsCurrentlyEdited() {
|
||||
P2PService p2PService = mock(P2PService.class);
|
||||
OfferBookService offerBookService = mock(OfferBookService.class);
|
||||
|
||||
when(p2PService.getPeerManager()).thenReturn(mock(PeerManager.class));
|
||||
|
||||
final OpenOfferManager manager = new OpenOfferManager(coreContext,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
p2PService,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
offerBookService,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
persistenceManager);
|
||||
|
||||
AtomicBoolean startEditOfferSuccessful = new AtomicBoolean(false);
|
||||
|
||||
ResultHandler resultHandler = () -> startEditOfferSuccessful.set(true);
|
||||
|
||||
final OpenOffer openOffer = new OpenOffer(make(btcUsdOffer));
|
||||
openOffer.setState(OpenOffer.State.DEACTIVATED);
|
||||
|
||||
manager.editOpenOfferStart(openOffer, resultHandler, null);
|
||||
assertTrue(startEditOfferSuccessful.get());
|
||||
|
||||
startEditOfferSuccessful.set(false);
|
||||
|
||||
manager.editOpenOfferStart(openOffer, resultHandler, null);
|
||||
assertTrue(startEditOfferSuccessful.get());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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.offer.availability;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ArbitratorSelectionTest {
|
||||
@Test
|
||||
public void testGetLeastUsedArbitrator() {
|
||||
// We get least used selected
|
||||
List<String> lastAddressesUsedInTrades;
|
||||
Set<String> arbitrators;
|
||||
String result;
|
||||
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb2", "arb1");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb1", "arb2"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb2", result);
|
||||
|
||||
// if all are same we use first according to alphanumeric sorting
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb2", "arb3");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb1", "arb2", "arb3"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb1", result);
|
||||
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb2", "arb3", "arb1");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb1", "arb2", "arb3"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb2", result);
|
||||
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb2", "arb3", "arb1", "arb2");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb1", "arb2", "arb3"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb3", result);
|
||||
|
||||
lastAddressesUsedInTrades = Arrays.asList("xxx", "ccc", "aaa");
|
||||
arbitrators = new HashSet<>(Arrays.asList("aaa", "ccc", "xxx"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("aaa", result);
|
||||
lastAddressesUsedInTrades = Arrays.asList("333", "000", "111");
|
||||
arbitrators = new HashSet<>(Arrays.asList("111", "333", "000"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("000", result);
|
||||
|
||||
// if winner is not in our arb list we use our arb from arbitrators even if never used in trades
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb2", "arb3");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb4"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb4", result);
|
||||
|
||||
// if winner (arb2) is not in our arb list we use our arb from arbitrators
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb1", "arb1", "arb2");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb1"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb1", result);
|
||||
|
||||
// arb1 is used least
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb2", "arb2", "arb2", "arb1", "arb1", "arb2");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb1", "arb2"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb1", result);
|
||||
}
|
||||
}
|
|
@ -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.payment;
|
||||
|
||||
import bisq.core.account.witness.AccountAgeWitness;
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class PaymentAccountsTest {
|
||||
@Test
|
||||
public void testGetOldestPaymentAccountForOfferWhenNoValidAccounts() {
|
||||
PaymentAccounts accounts = new PaymentAccounts(Collections.emptySet(), mock(AccountAgeWitnessService.class));
|
||||
PaymentAccount actual = accounts.getOldestPaymentAccountForOffer(mock(Offer.class));
|
||||
|
||||
assertNull(actual);
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testGetOldestPaymentAccountForOffer() {
|
||||
// AccountAgeWitnessService service = mock(AccountAgeWitnessService.class);
|
||||
//
|
||||
// PaymentAccount oldest = createAccountWithAge(service, 3);
|
||||
// Set<PaymentAccount> accounts = Sets.newHashSet(
|
||||
// oldest,
|
||||
// createAccountWithAge(service, 2),
|
||||
// createAccountWithAge(service, 1));
|
||||
//
|
||||
// BiFunction<Offer, PaymentAccount, Boolean> dummyValidator = (offer, account) -> true;
|
||||
// PaymentAccounts testedEntity = new PaymentAccounts(accounts, service, dummyValidator);
|
||||
//
|
||||
// PaymentAccount actual = testedEntity.getOldestPaymentAccountForOffer(mock(Offer.class));
|
||||
// assertEquals(oldest, actual);
|
||||
// }
|
||||
|
||||
private static PaymentAccount createAccountWithAge(AccountAgeWitnessService service, long age) {
|
||||
PaymentAccountPayload payload = mock(PaymentAccountPayload.class);
|
||||
|
||||
PaymentAccount account = mock(PaymentAccount.class);
|
||||
when(account.getPaymentAccountPayload()).thenReturn(payload);
|
||||
|
||||
AccountAgeWitness witness = mock(AccountAgeWitness.class);
|
||||
when(service.getAccountAge(eq(witness), any())).thenReturn(age);
|
||||
|
||||
when(service.getMyWitness(payload)).thenReturn(witness);
|
||||
|
||||
return account;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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.payment;
|
||||
|
||||
import bisq.core.locale.CryptoCurrency;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.payment.payload.PaymentMethod;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ReceiptPredicatesTest {
|
||||
private final ReceiptPredicates predicates = new ReceiptPredicates();
|
||||
|
||||
@Test
|
||||
public void testIsMatchingCurrency() {
|
||||
Offer offer = mock(Offer.class);
|
||||
when(offer.getCurrencyCode()).thenReturn("USD");
|
||||
|
||||
PaymentAccount account = mock(PaymentAccount.class);
|
||||
when(account.getTradeCurrencies()).thenReturn(Lists.newArrayList(
|
||||
new CryptoCurrency("BTC", "Bitcoin"),
|
||||
new CryptoCurrency("ETH", "Ether")));
|
||||
|
||||
assertFalse(predicates.isMatchingCurrency(offer, account));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsMatchingSepaOffer() {
|
||||
Offer offer = mock(Offer.class);
|
||||
PaymentMethod.SEPA = mock(PaymentMethod.class);
|
||||
when(offer.getPaymentMethod()).thenReturn(PaymentMethod.SEPA);
|
||||
|
||||
assertTrue(predicates.isMatchingSepaOffer(offer, mock(SepaInstantAccount.class)));
|
||||
assertTrue(predicates.isMatchingSepaOffer(offer, mock(SepaAccount.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsMatchingSepaInstant() {
|
||||
Offer offer = mock(Offer.class);
|
||||
PaymentMethod.SEPA_INSTANT = mock(PaymentMethod.class);
|
||||
when(offer.getPaymentMethod()).thenReturn(PaymentMethod.SEPA_INSTANT);
|
||||
|
||||
assertTrue(predicates.isMatchingSepaInstant(offer, mock(SepaInstantAccount.class)));
|
||||
assertFalse(predicates.isMatchingSepaInstant(offer, mock(SepaAccount.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsMatchingCountryCodes() {
|
||||
CountryBasedPaymentAccount account = mock(CountryBasedPaymentAccount.class);
|
||||
when(account.getCountry()).thenReturn(null);
|
||||
|
||||
assertFalse(predicates.isMatchingCountryCodes(mock(Offer.class), account));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSameOrSpecificBank() {
|
||||
PaymentMethod.SAME_BANK = mock(PaymentMethod.class);
|
||||
|
||||
Offer offer = mock(Offer.class);
|
||||
when(offer.getPaymentMethod()).thenReturn(PaymentMethod.SAME_BANK);
|
||||
|
||||
assertTrue(predicates.isOfferRequireSameOrSpecificBank(offer, mock(NationalBankAccount.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEqualPaymentMethods() {
|
||||
PaymentMethod method = PaymentMethod.getDummyPaymentMethod("1");
|
||||
|
||||
Offer offer = mock(Offer.class);
|
||||
when(offer.getPaymentMethod()).thenReturn(method);
|
||||
|
||||
PaymentAccount account = mock(PaymentAccount.class);
|
||||
when(account.getPaymentMethod()).thenReturn(method);
|
||||
|
||||
assertTrue(predicates.isEqualPaymentMethods(offer, account));
|
||||
}
|
||||
}
|
267
core/src/test/java/bisq/core/payment/ReceiptValidatorTest.java
Normal file
267
core/src/test/java/bisq/core/payment/ReceiptValidatorTest.java
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* 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.payment;
|
||||
|
||||
import bisq.core.offer.Offer;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.StrictStubs.class)
|
||||
public class ReceiptValidatorTest {
|
||||
private ReceiptValidator validator;
|
||||
private PaymentAccount account;
|
||||
private Offer offer;
|
||||
private ReceiptPredicates predicates;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.predicates = mock(ReceiptPredicates.class);
|
||||
this.account = mock(CountryBasedPaymentAccount.class);
|
||||
this.offer = mock(Offer.class);
|
||||
this.validator = new ReceiptValidator(offer, account, predicates);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
verifyZeroInteractions(offer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenCurrencyDoesNotMatch() {
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(false);
|
||||
|
||||
assertFalse(validator.isValid());
|
||||
verify(predicates).isMatchingCurrency(offer, account);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenNotCountryBasedAccount() {
|
||||
account = mock(PaymentAccount.class);
|
||||
assertFalse(account instanceof CountryBasedPaymentAccount);
|
||||
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(true);
|
||||
|
||||
assertTrue(new ReceiptValidator(offer, account, predicates).isValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenNotMatchingCodes() {
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(false);
|
||||
|
||||
assertFalse(validator.isValid());
|
||||
verify(predicates).isMatchingCountryCodes(offer, account);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenSepaOffer() {
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingSepaOffer(offer, account)).thenReturn(true);
|
||||
|
||||
assertTrue(validator.isValid());
|
||||
verify(predicates).isMatchingSepaOffer(offer, account);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenSepaInstant() {
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingSepaInstant(offer, account)).thenReturn(true);
|
||||
|
||||
assertTrue(validator.isValid());
|
||||
verify(predicates).isMatchingSepaOffer(offer, account);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenSpecificBankAccountAndOfferRequireSpecificBank() {
|
||||
account = mock(SpecificBanksAccount.class);
|
||||
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingSepaOffer(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingSepaInstant(offer, account)).thenReturn(false);
|
||||
when(predicates.isOfferRequireSameOrSpecificBank(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingBankId(offer, account)).thenReturn(false);
|
||||
|
||||
assertFalse(new ReceiptValidator(offer, account, predicates).isValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenSameBankAccountAndOfferRequireSpecificBank() {
|
||||
account = mock(SameBankAccount.class);
|
||||
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingSepaOffer(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingSepaInstant(offer, account)).thenReturn(false);
|
||||
when(predicates.isOfferRequireSameOrSpecificBank(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingBankId(offer, account)).thenReturn(false);
|
||||
|
||||
assertFalse(new ReceiptValidator(offer, account, predicates).isValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenSpecificBankAccount() {
|
||||
account = mock(SpecificBanksAccount.class);
|
||||
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingSepaOffer(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingSepaInstant(offer, account)).thenReturn(false);
|
||||
when(predicates.isOfferRequireSameOrSpecificBank(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingBankId(offer, account)).thenReturn(true);
|
||||
|
||||
assertTrue(new ReceiptValidator(offer, account, predicates).isValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenSameBankAccount() {
|
||||
account = mock(SameBankAccount.class);
|
||||
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingSepaOffer(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingSepaInstant(offer, account)).thenReturn(false);
|
||||
when(predicates.isOfferRequireSameOrSpecificBank(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingBankId(offer, account)).thenReturn(true);
|
||||
|
||||
assertTrue(new ReceiptValidator(offer, account, predicates).isValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenNationalBankAccount() {
|
||||
account = mock(NationalBankAccount.class);
|
||||
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingSepaOffer(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingSepaInstant(offer, account)).thenReturn(false);
|
||||
when(predicates.isOfferRequireSameOrSpecificBank(offer, account)).thenReturn(false);
|
||||
|
||||
assertTrue(new ReceiptValidator(offer, account, predicates).isValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
// Same or Specific Bank offers can't be taken by National Bank accounts. TODO: Consider partially relaxing to allow Specific Banks.
|
||||
public void testIsValidWhenNationalBankAccountAndOfferIsNot() {
|
||||
account = mock(NationalBankAccount.class);
|
||||
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingSepaOffer(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingSepaInstant(offer, account)).thenReturn(false);
|
||||
|
||||
assertFalse(new ReceiptValidator(offer, account, predicates).isValid());
|
||||
|
||||
verify(predicates, never()).isOfferRequireSameOrSpecificBank(offer, account);
|
||||
verify(predicates, never()).isMatchingBankId(offer, account);
|
||||
}
|
||||
|
||||
@Test
|
||||
// National or Same Bank offers can't be taken by Specific Banks accounts. TODO: Consider partially relaxing to allow National Bank.
|
||||
public void testIsValidWhenSpecificBanksAccountAndOfferIsNot() {
|
||||
account = mock(SpecificBanksAccount.class);
|
||||
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingSepaOffer(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingSepaInstant(offer, account)).thenReturn(false);
|
||||
|
||||
assertFalse(new ReceiptValidator(offer, account, predicates).isValid());
|
||||
|
||||
verify(predicates, never()).isOfferRequireSameOrSpecificBank(offer, account);
|
||||
verify(predicates, never()).isMatchingBankId(offer, account);
|
||||
}
|
||||
|
||||
@Test
|
||||
// National or Specific Bank offers can't be taken by Same Bank accounts.
|
||||
public void testIsValidWhenSameBankAccountAndOfferIsNot() {
|
||||
account = mock(SameBankAccount.class);
|
||||
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingSepaOffer(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingSepaInstant(offer, account)).thenReturn(false);
|
||||
|
||||
assertFalse(new ReceiptValidator(offer, account, predicates).isValid());
|
||||
|
||||
verify(predicates, never()).isOfferRequireSameOrSpecificBank(offer, account);
|
||||
verify(predicates, never()).isMatchingBankId(offer, account);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenWesternUnionAccount() {
|
||||
account = mock(WesternUnionAccount.class);
|
||||
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingSepaOffer(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingSepaInstant(offer, account)).thenReturn(false);
|
||||
when(predicates.isOfferRequireSameOrSpecificBank(offer, account)).thenReturn(false);
|
||||
|
||||
assertTrue(new ReceiptValidator(offer, account, predicates).isValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenWesternIrregularAccount() {
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingCountryCodes(offer, account)).thenReturn(true);
|
||||
when(predicates.isMatchingSepaOffer(offer, account)).thenReturn(false);
|
||||
when(predicates.isMatchingSepaInstant(offer, account)).thenReturn(false);
|
||||
when(predicates.isOfferRequireSameOrSpecificBank(offer, account)).thenReturn(false);
|
||||
|
||||
assertTrue(validator.isValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidWhenMoneyGramAccount() {
|
||||
account = mock(MoneyGramAccount.class);
|
||||
|
||||
when(predicates.isMatchingCurrency(offer, account)).thenReturn(true);
|
||||
when(predicates.isEqualPaymentMethods(offer, account)).thenReturn(true);
|
||||
|
||||
assertTrue(new ReceiptValidator(offer, account, predicates).isValid());
|
||||
|
||||
verify(predicates, never()).isMatchingCountryCodes(offer, account);
|
||||
verify(predicates, never()).isMatchingSepaOffer(offer, account);
|
||||
verify(predicates, never()).isMatchingSepaInstant(offer, account);
|
||||
verify(predicates, never()).isOfferRequireSameOrSpecificBank(offer, account);
|
||||
}
|
||||
}
|
50
core/src/test/java/bisq/core/payment/TradeLimitsTest.java
Normal file
50
core/src/test/java/bisq/core/payment/TradeLimitsTest.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.payment;
|
||||
|
||||
import bisq.core.dao.governance.period.PeriodService;
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class TradeLimitsTest {
|
||||
@Test
|
||||
public void testGetFirstMonthRiskBasedTradeLimit() {
|
||||
TradeLimits tradeLimits = new TradeLimits(mock(DaoStateService.class), mock(PeriodService.class));
|
||||
long expected, result;
|
||||
|
||||
expected = 0;
|
||||
result = tradeLimits.getFirstMonthRiskBasedTradeLimit(0, 1);
|
||||
assertEquals(expected, result);
|
||||
|
||||
expected = 25000000;
|
||||
result = tradeLimits.getFirstMonthRiskBasedTradeLimit(100000000, 1);
|
||||
assertEquals(expected, result);
|
||||
|
||||
expected = 3130000; //0.03125 -> 0.0313 -> 0.0313
|
||||
result = tradeLimits.getFirstMonthRiskBasedTradeLimit(100000000, 8);
|
||||
assertEquals(expected, result);
|
||||
|
||||
expected = 6250000;
|
||||
result = tradeLimits.getFirstMonthRiskBasedTradeLimit(200000000, 8);
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.payment.validation;
|
||||
|
||||
import bisq.core.locale.CurrencyUtil;
|
||||
import bisq.core.locale.Res;
|
||||
|
||||
import bisq.asset.AssetRegistry;
|
||||
|
||||
import bisq.common.config.BaseCurrencyNetwork;
|
||||
import bisq.common.config.Config;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class AltCoinAddressValidatorTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
AltCoinAddressValidator validator = new AltCoinAddressValidator(new AssetRegistry());
|
||||
|
||||
BaseCurrencyNetwork baseCurrencyNetwork = Config.baseCurrencyNetwork();
|
||||
String currencyCode = baseCurrencyNetwork.getCurrencyCode();
|
||||
Res.setBaseCurrencyCode(currencyCode);
|
||||
Res.setBaseCurrencyName(baseCurrencyNetwork.getCurrencyName());
|
||||
CurrencyUtil.setBaseCurrencyCode(currencyCode);
|
||||
|
||||
validator.setCurrencyCode("BTC");
|
||||
assertTrue(validator.validate("17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem").isValid);
|
||||
|
||||
validator.setCurrencyCode("LTC");
|
||||
assertTrue(validator.validate("Lg3PX8wRWmApFCoCMAsPF5P9dPHYQHEWKW").isValid);
|
||||
|
||||
validator.setCurrencyCode("BOGUS");
|
||||
|
||||
assertFalse(validator.validate("1BOGUSADDR").isValid);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* 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.provider.mempool;
|
||||
|
||||
import bisq.core.dao.governance.param.Param;
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
import bisq.core.util.ParsingUtils;
|
||||
import bisq.core.util.coin.BsqFormatter;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.Assert;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class TxValidatorTest {
|
||||
private static final Logger log = LoggerFactory.getLogger(TxValidatorTest.class);
|
||||
|
||||
private List<String> btcFeeReceivers = new ArrayList<>();
|
||||
|
||||
public TxValidatorTest() {
|
||||
btcFeeReceivers.add("1EKXx73oUhHaUh8JBimtiPGgHfwNmxYKAj");
|
||||
btcFeeReceivers.add("1HpvvMHcoXQsX85CjTsco5ZAAMoGu2Mze9");
|
||||
btcFeeReceivers.add("3EfRGckBQQuk7cpU7SwatPv8kFD1vALkTU");
|
||||
btcFeeReceivers.add("13sxMq8mTw7CTSqgGiMPfwo6ZDsVYrHLmR");
|
||||
btcFeeReceivers.add("19qA2BVPoyXDfHKVMovKG7SoxGY7xrBV8c");
|
||||
btcFeeReceivers.add("19BNi5EpZhgBBWAt5ka7xWpJpX2ZWJEYyq");
|
||||
btcFeeReceivers.add("38bZBj5peYS3Husdz7AH3gEUiUbYRD951t");
|
||||
btcFeeReceivers.add("3EtUWqsGThPtjwUczw27YCo6EWvQdaPUyp");
|
||||
btcFeeReceivers.add("1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7");
|
||||
btcFeeReceivers.add("3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV");
|
||||
btcFeeReceivers.add("34VLFgtFKAtwTdZ5rengTT2g2zC99sWQLC");
|
||||
log.warn("Known BTC fee receivers: {}", btcFeeReceivers.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMakerTx() throws InterruptedException {
|
||||
String mempoolData, offerData;
|
||||
|
||||
// paid the correct amount of BSQ fees
|
||||
offerData = "msimscqb,0636bafb14890edfb95465e66e2b1e15915f7fb595f9b653b9129c15ef4c1c4b,1000000,10,0,662390";
|
||||
mempoolData = "{\"txid\":\"0636bafb14890edfb95465e66e2b1e15915f7fb595f9b653b9129c15ef4c1c4b\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":7899}},{\"vout\":2,\"prevout\":{\"value\":54877439}}],\"vout\":[{\"scriptpubkey_address\":\"1FCUu7hqKCSsGhVJaLbGEoCWdZRJRNqq8w\",\"value\":7889},{\"scriptpubkey_address\":\"bc1qkj5l4wxl00ufdx6ygcnrck9fz5u927gkwqcgey\",\"value\":1600000},{\"scriptpubkey_address\":\"bc1qkw4a8u9l5w9fhdh3ue9v7e7celk4jyudzg5gk5\",\"value\":53276799}],\"size\":405,\"weight\":1287,\"fee\":650,\"status\":{\"confirmed\":true,\"block_height\":663140}}";
|
||||
Assert.assertTrue(createTxValidator(offerData).parseJsonValidateMakerFeeTx(mempoolData, btcFeeReceivers).getResult());
|
||||
|
||||
// UNDERPAID expected 1.01 BSQ, actual fee paid 0.80 BSQ (USED 8.00 RATE INSTEAD OF 10.06 RATE)
|
||||
// PASS due to leniency rule of accepting old DAO rate parameters: https://github.com/bisq-network/bisq/issues/5329#issuecomment-803223859
|
||||
offerData = "48067552,3b6009da764b71d79a4df8e2d8960b6919cae2e9bdccd5ef281e261fa9cd31b3,10000000,80,0,667656";
|
||||
mempoolData = "{\"txid\":\"3b6009da764b71d79a4df8e2d8960b6919cae2e9bdccd5ef281e261fa9cd31b3\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":9717}},{\"vout\":0,\"prevout\":{\"value\":4434912}},{\"vout\":2,\"prevout\":{\"value\":12809932}}],\"vout\":[{\"scriptpubkey_address\":\"1Nzqa4J7ck5bgz7QNXKtcjZExAvReozFo4\",\"value\":9637},{\"scriptpubkey_address\":\"bc1qhmmulf5prreqhccqy2wqpxxn6dcye7ame9dd57\",\"value\":11500000},{\"scriptpubkey_address\":\"bc1qx6hg8km2jdjc5ukhuedmkseu9wlsjtd8zeajpj\",\"value\":5721894}],\"size\":553,\"weight\":1879,\"fee\":23030,\"status\":{\"confirmed\":true,\"block_height\":667660}}";
|
||||
Assert.assertTrue(createTxValidator(offerData).parseJsonValidateMakerFeeTx(mempoolData, btcFeeReceivers).getResult());
|
||||
|
||||
// UNDERPAID Expected fee: 0.61 BSQ, actual fee paid: 0.35 BSQ (USED 5.75 RATE INSTEAD OF 10.06 RATE)
|
||||
// PASS due to leniency rule of accepting old DAO rate parameters: https://github.com/bisq-network/bisq/issues/5329#issuecomment-803223859
|
||||
offerData = "am7DzIv,4cdea8872a7d96210f378e0221dc1aae8ee9abb282582afa7546890fb39b7189,6100000,35,0,668195";
|
||||
mempoolData = "{\"txid\":\"4cdea8872a7d96210f378e0221dc1aae8ee9abb282582afa7546890fb39b7189\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":23893}},{\"vout\":1,\"prevout\":{\"value\":1440000}},{\"vout\":2,\"prevout\":{\"value\":16390881}}],\"vout\":[{\"scriptpubkey_address\":\"1Kmrzq3WGCQsZw5kroEphuk1KgsEr65yB7\",\"value\":23858},{\"scriptpubkey_address\":\"bc1qyw5qql9m7rkse9mhcun225nrjpwycszsa5dpjg\",\"value\":7015000},{\"scriptpubkey_address\":\"bc1q90y3p6mg0pe3rvvzfeudq4mfxafgpc9rulruff\",\"value\":10774186}],\"size\":554,\"weight\":1559,\"fee\":41730,\"status\":{\"confirmed\":true,\"block_height\":668198}}";
|
||||
Assert.assertTrue(createTxValidator(offerData).parseJsonValidateMakerFeeTx(mempoolData, btcFeeReceivers).getResult());
|
||||
|
||||
// UNDERPAID expected 0.11 BSQ, actual fee paid 0.08 BSQ (USED 5.75 RATE INSTEAD OF 7.53)
|
||||
// PASS due to leniency rule of accepting old DAO rate parameters: https://github.com/bisq-network/bisq/issues/5329#issuecomment-803223859
|
||||
offerData = "F1dzaFNQ,f72e263947c9dee6fbe7093fc85be34a149ef5bcfdd49b59b9cc3322fea8967b,1440000,8,0,670822, bsq paid too little";
|
||||
mempoolData = "{\"txid\":\"f72e263947c9dee6fbe7093fc85be34a149ef5bcfdd49b59b9cc3322fea8967b\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":15163}},{\"vout\":2,\"prevout\":{\"value\":6100000}}],\"vout\":[{\"scriptpubkey_address\":\"1MEsc2m4MSomNJWSr1p6fhnUQMyA3DRGrN\",\"value\":15155},{\"scriptpubkey_address\":\"bc1qztgwe9ry9a9puchjuscqdnv4v9lsm2ut0jtfec\",\"value\":2040000},{\"scriptpubkey_address\":\"bc1q0nstwxc0vqkj4x000xt328mfjapvlsd56nn70h\",\"value\":4048308}],\"size\":406,\"weight\":1291,\"fee\":11700,\"status\":{\"confirmed\":true,\"block_height\":670823}}";
|
||||
Assert.assertTrue(createTxValidator(offerData).parseJsonValidateMakerFeeTx(mempoolData, btcFeeReceivers).getResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTakerTx() throws InterruptedException {
|
||||
String mempoolData, offerData;
|
||||
|
||||
// The fee was more than what we expected: Expected BTC fee: 5000 sats , actual fee paid: 6000 sats
|
||||
offerData = "00072328,3524364062c96ba0280621309e8b539d152154422294c2cf263a965dcde9a8ca,1000000,6000,1,614672";
|
||||
mempoolData = "{\"txid\":\"3524364062c96ba0280621309e8b539d152154422294c2cf263a965dcde9a8ca\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":1,\"prevout\":{\"value\":2971000}}],\"vout\":[{\"scriptpubkey_address\":\"3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV\",\"value\":6000},{\"scriptpubkey_address\":\"1Hxu2X9Nr2fT3qEk9yjhiF54TJEz1Cxjoa\",\"value\":1607600},{\"scriptpubkey_address\":\"16VP6nHDDkmCMwaJj4PeyVHB88heDdVu9e\",\"value\":1353600}],\"size\":257,\"weight\":1028,\"fee\":3800,\"status\":{\"confirmed\":true,\"block_height\":614672}}";
|
||||
Assert.assertTrue(createTxValidator(offerData).parseJsonValidateTakerFeeTx(mempoolData, btcFeeReceivers).getResult());
|
||||
|
||||
// The fee matched what we expected
|
||||
offerData = "00072328,12f658954890d38ce698355be0b27fdd68d092c7b1b7475381918db060f46166,6250000,188,0,615955";
|
||||
mempoolData = "{\"txid\":\"12f658954890d38ce698355be0b27fdd68d092c7b1b7475381918db060f46166\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":19980}},{\"vout\":2,\"prevout\":{\"value\":2086015}},{\"vout\":0,\"prevout\":{\"value\":1100000}},{\"vout\":2,\"prevout\":{\"value\":938200}}],\"vout\":[{\"scriptpubkey_address\":\"17qiF1TYgT1YvsCPJyXQoKMtBZ7YJBW9GH\",\"value\":19792},{\"scriptpubkey_address\":\"16aFKD5hvEjJgPme5yRNJT2rAPdTXzdQc2\",\"value\":3768432},{\"scriptpubkey_address\":\"1D5V3QW8f5n4PhwfPgNkW9eWZwNJFyVU8n\",\"value\":346755}],\"size\":701,\"weight\":2804,\"fee\":9216,\"status\":{\"confirmed\":true,\"block_height\":615955}}";
|
||||
Assert.assertTrue(createTxValidator(offerData).parseJsonValidateTakerFeeTx(mempoolData, btcFeeReceivers).getResult());
|
||||
|
||||
// The fee was more than what we expected: Expected BTC fee: 5000 sats , actual fee paid: 7000 sats
|
||||
offerData = "bsqtrade,dfa4555ab78c657cad073e3f29c38c563d9dafc53afaa8c6af28510c734305c4,1000000,10,1,662390";
|
||||
mempoolData = "{\"txid\":\"dfa4555ab78c657cad073e3f29c38c563d9dafc53afaa8c6af28510c734305c4\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":678997}}],\"vout\":[{\"scriptpubkey_address\":\"3EfRGckBQQuk7cpU7SwatPv8kFD1vALkTU\",\"value\":7000},{\"scriptpubkey_address\":\"bc1qu6vey3e7flzg8gmhun05m43uc2vz0ay33kuu6r\",\"value\":647998}],\"size\":224,\"weight\":566,\"fee\":23999,\"status\":{\"confirmed\":true,\"block_height\":669720}}";
|
||||
Assert.assertTrue(createTxValidator(offerData).parseJsonValidateTakerFeeTx(mempoolData, btcFeeReceivers).getResult());
|
||||
|
||||
// The fee matched what we expected
|
||||
offerData = "89284,e1269aad63b3d894f5133ad658960971ef5c0fce6a13ad10544dc50fa3360588,900000,9,0,666473";
|
||||
mempoolData = "{\"txid\":\"e1269aad63b3d894f5133ad658960971ef5c0fce6a13ad10544dc50fa3360588\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":72738}},{\"vout\":0,\"prevout\":{\"value\":1600000}}],\"vout\":[{\"scriptpubkey_address\":\"17Kh5Ype9yNomqRrqu2k1mdV5c6FcKfGwQ\",\"value\":72691},{\"scriptpubkey_address\":\"bc1qdr9zcw7gf2sehxkux4fmqujm5uguhaqz7l9lca\",\"value\":629016},{\"scriptpubkey_address\":\"bc1qgqrrqv8q6l5d3t52fe28ghuhz4xqrsyxlwn03z\",\"value\":956523}],\"size\":404,\"weight\":1286,\"fee\":14508,\"status\":{\"confirmed\":true,\"block_height\":672388}}";
|
||||
Assert.assertTrue(createTxValidator(offerData).parseJsonValidateTakerFeeTx(mempoolData, btcFeeReceivers).getResult());
|
||||
|
||||
// UNDERPAID: Expected fee: 7.04 BSQ, actual fee paid: 1.01 BSQ
|
||||
offerData = "VOxRS,e99ea06aefc824fd45031447f7a0b56efb8117a09f9b8982e2c4da480a3a0e91,10000000,101,0,669129";
|
||||
mempoolData = "{\"txid\":\"e99ea06aefc824fd45031447f7a0b56efb8117a09f9b8982e2c4da480a3a0e91\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":16739}},{\"vout\":2,\"prevout\":{\"value\":113293809}}],\"vout\":[{\"scriptpubkey_address\":\"1F14nF6zoUfJkqZrFgdmK5VX5QVwEpAnKW\",\"value\":16638},{\"scriptpubkey_address\":\"bc1q80y688ev7u43vqy964yf7feqddvt2mkm8977cm\",\"value\":11500000},{\"scriptpubkey_address\":\"bc1q9whgyc2du9mrgnxz0nl0shwpw8ugrcae0j0w8p\",\"value\":101784485}],\"size\":406,\"weight\":1291,\"fee\":9425,\"status\":{\"confirmed\":true,\"block_height\":669134}}";
|
||||
Assert.assertFalse(createTxValidator(offerData).parseJsonValidateTakerFeeTx(mempoolData, btcFeeReceivers).getResult());
|
||||
|
||||
// UNDERPAID: Expected fee: 1029000 sats BTC, actual fee paid: 441000 sats BTC because they used the default rate of 0.003 should have been 0.007 per BTC
|
||||
// after 1.6.0 we introduce additional leniency to allow the default rate (which is not stored in the DAO param change list)
|
||||
offerData = "AKA,6779b7571f21a5a1af01d675bf032b8a3c571416d05345491018cbc2d016e888,147000000,441000,1,676543";
|
||||
mempoolData = "{'txid':'6779b7571f21a5a1af01d675bf032b8a3c571416d05345491018cbc2d016e888','version':1,'locktime':0,'vin':[{'txid':'94c36c0a9c5c99844ddfe17ef05a3ebbe94b14d76ee4bed7b00c7d45e681b441','vout':0,'prevout':{'scriptpubkey_address':'bc1qt5uprdzeh9g4el0k9cttn40qzagvpca9q0q6vl','value':177920825},'sequence':4294967295}],'vout':[{'scriptpubkey_address':'19BNi5EpZhgBBWAt5ka7xWpJpX2ZWJEYyq','value':441000},{'scriptpubkey_address':'bc1qxxcl9dz6usrx4z456g6fg8n3u9327hl458d6mx','value':177008388},{'scriptpubkey_address':'bc1qdq0894p2nmg04ceyqgapln6addfl80zy7z36jd','value':467243}],'size':256,'weight':697,'fee':4194,'status':{'confirmed':true,'block_height':676543}}";
|
||||
Assert.assertTrue(createTxValidator(offerData).parseJsonValidateTakerFeeTx(mempoolData, btcFeeReceivers).getResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGoodOffers() throws InterruptedException {
|
||||
Map<String, String> goodOffers = loadJsonTestData("offerTestData.json");
|
||||
Map<String, String> mempoolData = loadJsonTestData("txInfo.json");
|
||||
Assert.assertTrue(goodOffers.size() > 0);
|
||||
Assert.assertTrue(mempoolData.size() > 0);
|
||||
log.warn("TESTING GOOD OFFERS");
|
||||
testOfferSet(goodOffers, mempoolData, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadOffers() throws InterruptedException {
|
||||
Map<String, String> badOffers = loadJsonTestData("badOfferTestData.json");
|
||||
Map<String, String> mempoolData = loadJsonTestData("txInfo.json");
|
||||
Assert.assertTrue(badOffers.size() > 0);
|
||||
Assert.assertTrue(mempoolData.size() > 0);
|
||||
log.warn("TESTING BAD OFFERS");
|
||||
testOfferSet(badOffers, mempoolData, false);
|
||||
}
|
||||
|
||||
private void testOfferSet(Map<String, String> offers, Map<String, String> mempoolData, boolean expectedResult) {
|
||||
Set<String> knownValuesList = new HashSet<>(offers.values());
|
||||
knownValuesList.forEach(offerData -> {
|
||||
TxValidator txValidator = createTxValidator(offerData);
|
||||
log.warn("TESTING {}", txValidator.getTxId());
|
||||
String jsonTxt = mempoolData.get(txValidator.getTxId());
|
||||
if (jsonTxt == null || jsonTxt.isEmpty()) {
|
||||
log.warn("{} was not found in the mempool", txValidator.getTxId());
|
||||
Assert.assertFalse(expectedResult); // tx was not found in explorer
|
||||
} else {
|
||||
txValidator.parseJsonValidateMakerFeeTx(jsonTxt, btcFeeReceivers);
|
||||
Assert.assertTrue(expectedResult == txValidator.getResult());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Map<String, String> loadJsonTestData(String fileName) {
|
||||
String json = "";
|
||||
try {
|
||||
json = IOUtils.toString(this.getClass().getResourceAsStream(fileName), "UTF-8");
|
||||
} catch (IOException e) {
|
||||
log.error(e.toString());
|
||||
}
|
||||
Map<String, String> map = new Gson().fromJson(json, Map.class);
|
||||
return map;
|
||||
}
|
||||
|
||||
// initialize the TxValidator with offerData to be validated
|
||||
// and mock the used DaoStateService
|
||||
private TxValidator createTxValidator(String offerData) {
|
||||
try {
|
||||
String[] y = offerData.split(",");
|
||||
String txId = y[1];
|
||||
long amount = Long.parseLong(y[2]);
|
||||
boolean isCurrencyForMakerFeeBtc = Long.parseLong(y[4]) > 0;
|
||||
DaoStateService mockedDaoStateService = mock(DaoStateService.class);
|
||||
|
||||
Answer<Coin> mockGetFeeRate = invocation -> {
|
||||
return mockedLookupFeeRate(invocation.getArgument(0), invocation.getArgument(1));
|
||||
};
|
||||
Answer<Coin> mockGetParamValueAsCoin = invocation -> {
|
||||
return mockedGetParamValueAsCoin(invocation.getArgument(0), invocation.getArgument(1));
|
||||
};
|
||||
Answer<List<Coin>> mockGetParamChangeList = invocation -> {
|
||||
return mockedGetParamChangeList(invocation.getArgument(0));
|
||||
};
|
||||
when(mockedDaoStateService.getParamValueAsCoin(Mockito.any(Param.class), Mockito.anyInt())).thenAnswer(mockGetFeeRate);
|
||||
when(mockedDaoStateService.getParamValueAsCoin(Mockito.any(Param.class), Mockito.anyString())).thenAnswer(mockGetParamValueAsCoin);
|
||||
when(mockedDaoStateService.getParamChangeList(Mockito.any())).thenAnswer(mockGetParamChangeList);
|
||||
TxValidator txValidator = new TxValidator(mockedDaoStateService, txId, Coin.valueOf(amount), isCurrencyForMakerFeeBtc);
|
||||
return txValidator;
|
||||
} catch (RuntimeException ignore) {
|
||||
// If input format is not as expected we ignore entry
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Coin mockedLookupFeeRate(Param param, int blockHeight) {
|
||||
BsqFormatter bsqFormatter = new BsqFormatter();
|
||||
LinkedHashMap<Long, String> feeMap = mockedGetFeeRateMap(param);
|
||||
for (Map.Entry<Long, String> entry : feeMap.entrySet()) {
|
||||
if (blockHeight >= entry.getKey()) {
|
||||
if (param.equals(Param.DEFAULT_MAKER_FEE_BTC) || param.equals(Param.DEFAULT_TAKER_FEE_BTC))
|
||||
return bsqFormatter.parseToBTC(entry.getValue());
|
||||
else
|
||||
return ParsingUtils.parseToCoin(entry.getValue(), bsqFormatter);
|
||||
}
|
||||
}
|
||||
if (param.equals(Param.DEFAULT_MAKER_FEE_BTC) || param.equals(Param.DEFAULT_TAKER_FEE_BTC))
|
||||
return bsqFormatter.parseToBTC(param.getDefaultValue());
|
||||
else
|
||||
return ParsingUtils.parseToCoin(param.getDefaultValue(), bsqFormatter);
|
||||
}
|
||||
|
||||
private LinkedHashMap<Long, String> mockedGetFeeRateMap(Param param) {
|
||||
LinkedHashMap<Long, String> feeMap = new LinkedHashMap<>();
|
||||
if (param == Param.DEFAULT_MAKER_FEE_BSQ) {
|
||||
feeMap.put(674707L, "8.66"); // https://github.com/bisq-network/proposals/issues/318
|
||||
feeMap.put(670027L, "7.53");
|
||||
feeMap.put(660667L, "10.06");
|
||||
feeMap.put(655987L, "8.74");
|
||||
feeMap.put(641947L, "7.6");
|
||||
feeMap.put(632587L, "6.6");
|
||||
feeMap.put(623227L, "5.75");
|
||||
feeMap.put(599827L, "10.0");
|
||||
feeMap.put(590467L, "13.0");
|
||||
feeMap.put(585787L, "8.0");
|
||||
feeMap.put(581107L, "1.6");
|
||||
} else if (param == Param.DEFAULT_TAKER_FEE_BSQ) {
|
||||
feeMap.put(674707L, "60.59"); // https://github.com/bisq-network/proposals/issues/318
|
||||
feeMap.put(670027L, "52.68");
|
||||
feeMap.put(660667L, "70.39");
|
||||
feeMap.put(655987L, "61.21");
|
||||
feeMap.put(641947L, "53.23");
|
||||
feeMap.put(632587L, "46.30");
|
||||
feeMap.put(623227L, "40.25");
|
||||
feeMap.put(599827L, "30.00");
|
||||
feeMap.put(590467L, "38.00");
|
||||
feeMap.put(585787L, "24.00");
|
||||
feeMap.put(581107L, "4.80");
|
||||
} else if (param == Param.DEFAULT_MAKER_FEE_BTC) {
|
||||
feeMap.put(623227L, "0.0010");
|
||||
feeMap.put(585787L, "0.0020");
|
||||
} else if (param == Param.DEFAULT_TAKER_FEE_BTC) {
|
||||
feeMap.put(623227L, "0.0070");
|
||||
feeMap.put(585787L, "0.0060");
|
||||
}
|
||||
return feeMap;
|
||||
}
|
||||
|
||||
public Coin mockedGetParamValueAsCoin(Param param, String paramValue) {
|
||||
BsqFormatter bsqFormatter = new BsqFormatter();
|
||||
return bsqFormatter.parseParamValueToCoin(param, paramValue);
|
||||
}
|
||||
|
||||
public List<Coin> mockedGetParamChangeList(Param param) {
|
||||
BsqFormatter bsqFormatter = new BsqFormatter();
|
||||
List<Coin> retVal = new ArrayList<Coin>();
|
||||
Map<Long, String> feeMap = mockedGetFeeRateMap(param);
|
||||
for (Map.Entry<Long, String> entry : feeMap.entrySet()) {
|
||||
retVal.add(ParsingUtils.parseToCoin(entry.getValue(), bsqFormatter));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.provider.price;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
@Ignore
|
||||
public class MarketPriceFeedServiceTest {
|
||||
private static final Logger log = LoggerFactory.getLogger(MarketPriceFeedServiceTest.class);
|
||||
|
||||
@Test
|
||||
public void testGetPrice() throws InterruptedException {
|
||||
PriceFeedService priceFeedService = new PriceFeedService(null, null, null);
|
||||
priceFeedService.setCurrencyCode("EUR");
|
||||
priceFeedService.requestPriceFeed(tradeCurrency -> {
|
||||
log.debug(tradeCurrency.toString());
|
||||
assertTrue(true);
|
||||
},
|
||||
(errorMessage, throwable) -> {
|
||||
log.debug(errorMessage);
|
||||
assertTrue(false);
|
||||
}
|
||||
);
|
||||
Thread.sleep(10000);
|
||||
}
|
||||
}
|
48
core/src/test/java/bisq/core/trade/TradableListTest.java
Normal file
48
core/src/test/java/bisq/core/trade/TradableListTest.java
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.offer.Offer;
|
||||
import bisq.core.offer.OfferPayload;
|
||||
import bisq.core.offer.OpenOffer;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static protobuf.PersistableEnvelope.MessageCase.TRADABLE_LIST;
|
||||
|
||||
public class TradableListTest {
|
||||
|
||||
@Test
|
||||
public void protoTesting() {
|
||||
OfferPayload offerPayload = mock(OfferPayload.class, RETURNS_DEEP_STUBS);
|
||||
TradableList<OpenOffer> openOfferTradableList = new TradableList<>();
|
||||
protobuf.PersistableEnvelope message = (protobuf.PersistableEnvelope) openOfferTradableList.toProtoMessage();
|
||||
assertEquals(message.getMessageCase(), TRADABLE_LIST);
|
||||
|
||||
// test adding an OpenOffer and convert toProto
|
||||
Offer offer = new Offer(offerPayload);
|
||||
OpenOffer openOffer = new OpenOffer(offer);
|
||||
openOfferTradableList.add(openOffer);
|
||||
message = (protobuf.PersistableEnvelope) openOfferTradableList.toProtoMessage();
|
||||
assertEquals(message.getMessageCase(), TRADABLE_LIST);
|
||||
assertEquals(1, message.getTradableList().getTradableList().size());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
package bisq.core.trade.txproof.xmr;
|
||||
|
||||
import bisq.core.user.AutoConfirmSettings;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static bisq.core.trade.txproof.xmr.XmrTxProofParser.MAX_DATE_TOLERANCE;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
public class XmrTxProofParserTest {
|
||||
private XmrTxProofModel xmrTxProofModel;
|
||||
private String recipientAddressHex = "e957dac72bcec80d59b2fecacfa7522223b6a5df895b7e388e60297e85f3f867b42f43e8d9f086a99a997704ceb92bd9cd99d33952de90c9f5f93c82c62360ae";
|
||||
private String txHash = "488e48ab0c7e69028d19f787ec57fd496ff114caba9ab265bfd41a3ea0e4687d";
|
||||
private String txKey = "6c336e52ed537676968ee319af6983c80b869ca6a732b5962c02748b486f8f0f";
|
||||
private XmrTxProofParser parser;
|
||||
private Date tradeDate;
|
||||
|
||||
@Before
|
||||
public void prepareMocksAndObjects() {
|
||||
long amount = 100000000000L;
|
||||
tradeDate = new Date(1574922644000L);
|
||||
String serviceAddress = "127.0.0.1:8081";
|
||||
AutoConfirmSettings autoConfirmSettings = new AutoConfirmSettings(true,
|
||||
10,
|
||||
1,
|
||||
Collections.singletonList(serviceAddress),
|
||||
"XMR");
|
||||
|
||||
// TODO using the mocking framework would be better...
|
||||
String recipientAddress = "4ATyxmFGU7h3EWu5kYR6gy6iCNFCftbsjATfbuBBjsRHJM4KTwEyeiyVNNUmsfpK1kdRxs8QoPLsZanGqe1Mby43LeyWNMF";
|
||||
xmrTxProofModel = new XmrTxProofModel(
|
||||
"dummyTest",
|
||||
txHash,
|
||||
txKey,
|
||||
recipientAddress,
|
||||
amount,
|
||||
tradeDate,
|
||||
autoConfirmSettings);
|
||||
|
||||
parser = new XmrTxProofParser();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonRoot() {
|
||||
// checking what happens when bad input is provided
|
||||
assertSame(parser.parse(xmrTxProofModel,
|
||||
"invalid json data").getDetail(), XmrTxProofRequest.Detail.API_INVALID);
|
||||
assertSame(parser.parse(xmrTxProofModel,
|
||||
"").getDetail(), XmrTxProofRequest.Detail.API_INVALID);
|
||||
assertSame(parser.parse(xmrTxProofModel,
|
||||
"[]").getDetail(), XmrTxProofRequest.Detail.API_INVALID);
|
||||
assertSame(parser.parse(xmrTxProofModel,
|
||||
"{}").getDetail(), XmrTxProofRequest.Detail.API_INVALID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonTopLevel() {
|
||||
// testing the top level fields: data and status
|
||||
assertSame(parser.parse(xmrTxProofModel,
|
||||
"{'data':{'title':''},'status':'fail'}")
|
||||
.getDetail(), XmrTxProofRequest.Detail.TX_NOT_FOUND);
|
||||
assertSame(parser.parse(xmrTxProofModel,
|
||||
"{'data':{'title':''},'missingstatus':'success'}")
|
||||
.getDetail(), XmrTxProofRequest.Detail.API_INVALID);
|
||||
assertSame(parser.parse(xmrTxProofModel,
|
||||
"{'missingdata':{'title':''},'status':'success'}")
|
||||
.getDetail(), XmrTxProofRequest.Detail.API_INVALID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonAddress() {
|
||||
assertSame(parser.parse(xmrTxProofModel,
|
||||
"{'data':{'missingaddress':'irrelevant'},'status':'success'}")
|
||||
.getDetail(), XmrTxProofRequest.Detail.API_INVALID);
|
||||
assertSame(parser.parse(xmrTxProofModel,
|
||||
"{'data':{'address':'e957dac7'},'status':'success'}")
|
||||
.getDetail(), XmrTxProofRequest.Detail.ADDRESS_INVALID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonTxHash() {
|
||||
String missing_tx_hash = "{'data':{'address':'" + recipientAddressHex + "'}, 'status':'success'}";
|
||||
assertSame(parser.parse(xmrTxProofModel, missing_tx_hash).getDetail(), XmrTxProofRequest.Detail.API_INVALID);
|
||||
|
||||
String invalid_tx_hash = "{'data':{'address':'" + recipientAddressHex + "', 'tx_hash':'488e48'}, 'status':'success'}";
|
||||
assertSame(parser.parse(xmrTxProofModel, invalid_tx_hash).getDetail(), XmrTxProofRequest.Detail.TX_HASH_INVALID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonTxKey() {
|
||||
String missing_tx_key = "{'data':{'address':'" + recipientAddressHex + "', " +
|
||||
"'tx_hash':'" + txHash + "'}, 'status':'success'}";
|
||||
assertSame(parser.parse(xmrTxProofModel, missing_tx_key).getDetail(), XmrTxProofRequest.Detail.API_INVALID);
|
||||
|
||||
String invalid_tx_key = "{'data':{'address':'" + recipientAddressHex + "', " +
|
||||
"'tx_hash':'" + txHash + "', " +
|
||||
"'viewkey':'cdce04'}, 'status':'success'}";
|
||||
assertSame(parser.parse(xmrTxProofModel, invalid_tx_key).getDetail(), XmrTxProofRequest.Detail.TX_KEY_INVALID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonTxTimestamp() {
|
||||
String missing_tx_timestamp = "{'data':{'address':'" + recipientAddressHex + "', " +
|
||||
"'tx_hash':'" + txHash + "'," +
|
||||
"'viewkey':'" + txKey + "'}, 'status':'success'}";
|
||||
assertSame(parser.parse(xmrTxProofModel, missing_tx_timestamp).getDetail(), XmrTxProofRequest.Detail.API_INVALID);
|
||||
|
||||
String invalid_tx_timestamp = "{'data':{'address':'" + recipientAddressHex + "', " +
|
||||
"'tx_hash':'" + txHash + "', " +
|
||||
"'viewkey':'" + txKey + "'," +
|
||||
"'tx_timestamp':'12345'}, 'status':'success'}";
|
||||
assertSame(parser.parse(xmrTxProofModel, invalid_tx_timestamp).getDetail(), XmrTxProofRequest.Detail.TRADE_DATE_NOT_MATCHING);
|
||||
|
||||
long tradeTimeSec = tradeDate.getTime() / 1000;
|
||||
String ts = String.valueOf(tradeTimeSec - MAX_DATE_TOLERANCE - 1);
|
||||
String invalid_tx_timestamp_1ms_too_old = "{'data':{'address':'" + recipientAddressHex + "', " +
|
||||
"'tx_hash':'" + txHash + "', " +
|
||||
"'viewkey':'" + txKey + "'," +
|
||||
"'tx_timestamp':'" + ts + "'}, 'status':'success'}";
|
||||
assertSame(parser.parse(xmrTxProofModel, invalid_tx_timestamp_1ms_too_old).getDetail(), XmrTxProofRequest.Detail.TRADE_DATE_NOT_MATCHING);
|
||||
|
||||
ts = String.valueOf(tradeTimeSec - MAX_DATE_TOLERANCE);
|
||||
String valid_tx_timestamp_exact_MAX_DATE_TOLERANCE = "{'data':{'address':'" + recipientAddressHex + "', " +
|
||||
"'tx_hash':'" + txHash + "', " +
|
||||
"'viewkey':'" + txKey + "'," +
|
||||
"'tx_timestamp':'" + ts + "'}, 'status':'success'}";
|
||||
parser.parse(xmrTxProofModel, valid_tx_timestamp_exact_MAX_DATE_TOLERANCE);
|
||||
assertNotSame(parser.parse(xmrTxProofModel, valid_tx_timestamp_exact_MAX_DATE_TOLERANCE).getDetail(), XmrTxProofRequest.Detail.TRADE_DATE_NOT_MATCHING);
|
||||
|
||||
ts = String.valueOf(tradeTimeSec - MAX_DATE_TOLERANCE + 1);
|
||||
String valid_tx_timestamp_less_than_MAX_DATE_TOLERANCE = "{'data':{'address':'" + recipientAddressHex + "', " +
|
||||
"'tx_hash':'" + txHash + "', " +
|
||||
"'viewkey':'" + txKey + "'," +
|
||||
"'tx_timestamp':'" + ts + "'}, 'status':'success'}";
|
||||
assertNotSame(parser.parse(xmrTxProofModel, valid_tx_timestamp_less_than_MAX_DATE_TOLERANCE).getDetail(), XmrTxProofRequest.Detail.TRADE_DATE_NOT_MATCHING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonTxConfirmation() {
|
||||
long epochDate = Instant.now().toEpochMilli() / 1000;
|
||||
String outputs = "'outputs':[" +
|
||||
"{'amount':100000000000,'match':true,'output_idx':0,'output_pubkey':'972a2c9178876f1fae4ecd22f9d7c132a12706db8ffb5d1f223f9aa8ced75b61'}," +
|
||||
"{'amount':0,'match':false,'output_idx':1,'output_pubkey':'658330d2d56c74aca3b40900c56cd0f0111e2876be677ade493d06d539a1bab0'}],";
|
||||
String json = "{'status':'success', 'data':{" +
|
||||
"'address':'" + recipientAddressHex + "', " +
|
||||
outputs +
|
||||
"'tx_confirmations':777, " +
|
||||
"'tx_hash':'" + txHash + "', " +
|
||||
"'viewkey':'" + txKey + "', " +
|
||||
"'tx_timestamp':'" + epochDate + "'}" +
|
||||
"}";
|
||||
assertSame(parser.parse(xmrTxProofModel, json), XmrTxProofRequest.Result.SUCCESS);
|
||||
json = json.replaceFirst("777", "0");
|
||||
|
||||
assertSame(parser.parse(xmrTxProofModel, json).getDetail(), XmrTxProofRequest.Detail.PENDING_CONFIRMATIONS);
|
||||
|
||||
json = json.replaceFirst("100000000000", "100000000001");
|
||||
assertSame(parser.parse(xmrTxProofModel, json).getDetail(), XmrTxProofRequest.Detail.AMOUNT_NOT_MATCHING);
|
||||
|
||||
// Revert change of amount
|
||||
json = json.replaceFirst("100000000001", "100000000000");
|
||||
json = json.replaceFirst("'match':true", "'match':false");
|
||||
assertSame(parser.parse(xmrTxProofModel, json).getDetail(), XmrTxProofRequest.Detail.NO_MATCH_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonFail() {
|
||||
String failedJson = "{\"data\":null,\"message\":\"Cant parse tx hash: a\",\"status\":\"error\"}";
|
||||
assertSame(parser.parse(xmrTxProofModel, failedJson).getDetail(), XmrTxProofRequest.Detail.API_INVALID);
|
||||
}
|
||||
}
|
142
core/src/test/java/bisq/core/user/PreferencesTest.java
Normal file
142
core/src/test/java/bisq/core/user/PreferencesTest.java
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* 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.user;
|
||||
|
||||
import bisq.core.btc.nodes.LocalBitcoinNode;
|
||||
import bisq.core.locale.CountryUtil;
|
||||
import bisq.core.locale.CryptoCurrency;
|
||||
import bisq.core.locale.CurrencyUtil;
|
||||
import bisq.core.locale.FiatCurrency;
|
||||
import bisq.core.locale.GlobalSettings;
|
||||
import bisq.core.locale.Res;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
import bisq.common.persistence.PersistenceManager;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class PreferencesTest {
|
||||
|
||||
private Preferences preferences;
|
||||
private PersistenceManager persistenceManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
final Locale en_US = new Locale("en", "US");
|
||||
Locale.setDefault(en_US);
|
||||
GlobalSettings.setLocale(en_US);
|
||||
Res.setBaseCurrencyCode("BTC");
|
||||
Res.setBaseCurrencyName("Bitcoin");
|
||||
|
||||
persistenceManager = mock(PersistenceManager.class);
|
||||
Config config = new Config();
|
||||
LocalBitcoinNode localBitcoinNode = new LocalBitcoinNode(config);
|
||||
preferences = new Preferences(
|
||||
persistenceManager, config, null, localBitcoinNode, null, null, Config.DEFAULT_FULL_DAO_NODE,
|
||||
null, null, Config.UNSPECIFIED_PORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddFiatCurrency() {
|
||||
final FiatCurrency usd = new FiatCurrency("USD");
|
||||
final FiatCurrency usd2 = new FiatCurrency("USD");
|
||||
final ObservableList<FiatCurrency> fiatCurrencies = preferences.getFiatCurrenciesAsObservable();
|
||||
|
||||
preferences.addFiatCurrency(usd);
|
||||
|
||||
assertEquals(1, fiatCurrencies.size());
|
||||
|
||||
preferences.addFiatCurrency(usd2);
|
||||
|
||||
assertEquals(1, fiatCurrencies.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUniqueListOfFiatCurrencies() {
|
||||
PreferencesPayload payload = mock(PreferencesPayload.class);
|
||||
|
||||
List<FiatCurrency> fiatCurrencies = CurrencyUtil.getMainFiatCurrencies();
|
||||
final FiatCurrency usd = new FiatCurrency("USD");
|
||||
fiatCurrencies.add(usd);
|
||||
|
||||
when(persistenceManager.getPersisted(anyString())).thenReturn(payload);
|
||||
when(payload.getUserLanguage()).thenReturn("en");
|
||||
when(payload.getUserCountry()).thenReturn(CountryUtil.getDefaultCountry());
|
||||
when(payload.getPreferredTradeCurrency()).thenReturn(usd);
|
||||
when(payload.getFiatCurrencies()).thenReturn(fiatCurrencies);
|
||||
|
||||
preferences.readPersisted(() -> {
|
||||
assertEquals(7, preferences.getFiatCurrenciesAsObservable().size());
|
||||
assertTrue(preferences.getFiatCurrenciesAsObservable().contains(usd));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUniqueListOfCryptoCurrencies() {
|
||||
PreferencesPayload payload = mock(PreferencesPayload.class);
|
||||
|
||||
List<CryptoCurrency> cryptoCurrencies = CurrencyUtil.getMainCryptoCurrencies();
|
||||
final CryptoCurrency dash = new CryptoCurrency("DASH", "Dash");
|
||||
cryptoCurrencies.add(dash);
|
||||
|
||||
when(persistenceManager.getPersisted(anyString())).thenReturn(payload);
|
||||
when(payload.getUserLanguage()).thenReturn("en");
|
||||
when(payload.getUserCountry()).thenReturn(CountryUtil.getDefaultCountry());
|
||||
when(payload.getPreferredTradeCurrency()).thenReturn(new FiatCurrency("USD"));
|
||||
when(payload.getCryptoCurrencies()).thenReturn(cryptoCurrencies);
|
||||
|
||||
preferences.readPersisted(() -> {
|
||||
assertTrue(preferences.getCryptoCurrenciesAsObservable().contains(dash));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateOfPersistedFiatCurrenciesAfterLocaleChanged() {
|
||||
PreferencesPayload payload = mock(PreferencesPayload.class);
|
||||
|
||||
List<FiatCurrency> fiatCurrencies = new ArrayList<>();
|
||||
final FiatCurrency usd = new FiatCurrency(Currency.getInstance("USD"), new Locale("de", "AT"));
|
||||
fiatCurrencies.add(usd);
|
||||
|
||||
assertEquals("US-Dollar (USD)", usd.getNameAndCode());
|
||||
|
||||
when(persistenceManager.getPersisted(anyString())).thenReturn(payload);
|
||||
when(payload.getUserLanguage()).thenReturn("en");
|
||||
when(payload.getUserCountry()).thenReturn(CountryUtil.getDefaultCountry());
|
||||
when(payload.getPreferredTradeCurrency()).thenReturn(usd);
|
||||
when(payload.getFiatCurrencies()).thenReturn(fiatCurrencies);
|
||||
|
||||
preferences.readPersisted(() -> {
|
||||
assertEquals("US Dollar (USD)", preferences.getFiatCurrenciesAsObservable().get(0).getNameAndCode());
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.user;
|
||||
|
||||
import bisq.core.alert.Alert;
|
||||
import bisq.core.arbitration.ArbitratorTest;
|
||||
import bisq.core.arbitration.MediatorTest;
|
||||
import bisq.core.filter.Filter;
|
||||
import bisq.core.proto.CoreProtoResolver;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.Ignore;
|
||||
|
||||
public class UserPayloadModelVOTest {
|
||||
@Ignore("TODO InvalidKeySpecException at bisq.common.crypto.Sig.getPublicKeyFromBytes(Sig.java:135)")
|
||||
public void testRoundtrip() {
|
||||
UserPayload vo = new UserPayload();
|
||||
vo.setAccountId("accountId");
|
||||
UserPayload newVo = UserPayload.fromProto(vo.toProtoMessage().getUserPayload(), new CoreProtoResolver());
|
||||
}
|
||||
|
||||
@Ignore("TODO InvalidKeySpecException at bisq.common.crypto.Sig.getPublicKeyFromBytes(Sig.java:135)")
|
||||
public void testRoundtripFull() {
|
||||
UserPayload vo = new UserPayload();
|
||||
vo.setAccountId("accountId");
|
||||
vo.setDisplayedAlert(new Alert("message", true, false, "version", new byte[]{12, -64, 12}, "string", null));
|
||||
vo.setDevelopersFilter(new Filter(Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
false,
|
||||
Lists.newArrayList(),
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
Lists.newArrayList(),
|
||||
new HashSet<>(),
|
||||
false,
|
||||
false));
|
||||
|
||||
vo.setRegisteredArbitrator(ArbitratorTest.getArbitratorMock());
|
||||
vo.setRegisteredMediator(MediatorTest.getMediatorMock());
|
||||
vo.setAcceptedArbitrators(Lists.newArrayList(ArbitratorTest.getArbitratorMock()));
|
||||
vo.setAcceptedMediators(Lists.newArrayList(MediatorTest.getMediatorMock()));
|
||||
UserPayload newVo = UserPayload.fromProto(vo.toProtoMessage().getUserPayload(), new CoreProtoResolver());
|
||||
}
|
||||
}
|
147
core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java
Normal file
147
core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import bisq.core.dao.DaoFacade;
|
||||
import bisq.core.dao.governance.param.Param;
|
||||
import bisq.core.filter.Filter;
|
||||
import bisq.core.filter.FilterManager;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class FeeReceiverSelectorTest {
|
||||
@Mock
|
||||
private DaoFacade daoFacade;
|
||||
@Mock
|
||||
private FilterManager filterManager;
|
||||
|
||||
@Test
|
||||
public void testGetAddress() {
|
||||
Random rnd = new Random(123);
|
||||
when(filterManager.getFilter()).thenReturn(filterWithReceivers(
|
||||
List.of("", "foo#0.001", "ill-formed", "bar#0.002", "baz#0.001", "partial#bad")));
|
||||
|
||||
Map<String, Integer> selectionCounts = new HashMap<>();
|
||||
for (int i = 0; i < 400; i++) {
|
||||
String address = FeeReceiverSelector.getAddress(daoFacade, filterManager, rnd);
|
||||
selectionCounts.compute(address, (k, n) -> n != null ? n + 1 : 1);
|
||||
}
|
||||
|
||||
assertEquals(3, selectionCounts.size());
|
||||
|
||||
// Check within 2 std. of the expected values (95% confidence each):
|
||||
assertEquals(100.0, selectionCounts.get("foo"), 18);
|
||||
assertEquals(200.0, selectionCounts.get("bar"), 20);
|
||||
assertEquals(100.0, selectionCounts.get("baz"), 18);
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWeightedSelection() {
|
||||
Random rnd = new Random(456);
|
||||
|
||||
int[] selections = new int[3];
|
||||
for (int i = 0; i < 6000; i++) {
|
||||
selections[FeeReceiverSelector.weightedSelection(Longs.asList(1, 2, 3), rnd)]++;
|
||||
}
|
||||
|
||||
// Check within 2 std. of the expected values (95% confidence each):
|
||||
assertEquals(1000.0, selections[0], 58);
|
||||
assertEquals(2000.0, selections[1], 74);
|
||||
assertEquals(3000.0, selections[2], 78);
|
||||
}
|
||||
|
||||
private static Filter filterWithReceivers(List<String> btcFeeReceiverAddresses) {
|
||||
return new Filter(Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
false,
|
||||
Lists.newArrayList(),
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
btcFeeReceiverAddresses,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
Lists.newArrayList(),
|
||||
new HashSet<>(),
|
||||
false,
|
||||
false);
|
||||
}
|
||||
}
|
69
core/src/test/java/bisq/core/util/FormattingUtilsTest.java
Normal file
69
core/src/test/java/bisq/core/util/FormattingUtilsTest.java
Normal file
|
@ -0,0 +1,69 @@
|
|||
package bisq.core.util;
|
||||
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.monetary.Altcoin;
|
||||
import bisq.core.monetary.Price;
|
||||
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.natpryce.makeiteasy.Instantiator;
|
||||
import com.natpryce.makeiteasy.Maker;
|
||||
import com.natpryce.makeiteasy.Property;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.natpryce.makeiteasy.MakeItEasy.a;
|
||||
import static com.natpryce.makeiteasy.MakeItEasy.make;
|
||||
import static com.natpryce.makeiteasy.MakeItEasy.with;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class FormattingUtilsTest {
|
||||
private static final Property<Price, String> currencyCode = new Property<>();
|
||||
private static final Property<Price, String> priceString = new Property<>();
|
||||
private static final Maker<Price> usdPrice = a(lookup ->
|
||||
new Price(Fiat.parseFiat(lookup.valueOf(currencyCode, "USD"), lookup.valueOf(priceString, "100"))));
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Locale.setDefault(new Locale("en", "US"));
|
||||
Res.setBaseCurrencyCode("BTC");
|
||||
Res.setBaseCurrencyName("Bitcoin");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatDurationAsWords() {
|
||||
long oneDay = TimeUnit.DAYS.toMillis(1);
|
||||
long oneHour = TimeUnit.HOURS.toMillis(1);
|
||||
long oneMinute = TimeUnit.MINUTES.toMillis(1);
|
||||
long oneSecond = TimeUnit.SECONDS.toMillis(1);
|
||||
|
||||
assertEquals("1 hour, 0 minutes", FormattingUtils.formatDurationAsWords(oneHour));
|
||||
assertEquals("1 day, 0 hours, 0 minutes", FormattingUtils.formatDurationAsWords(oneDay));
|
||||
assertEquals("2 days, 0 hours, 1 minute", FormattingUtils.formatDurationAsWords(oneDay * 2 + oneMinute));
|
||||
assertEquals("2 days, 0 hours, 2 minutes", FormattingUtils.formatDurationAsWords(oneDay * 2 + oneMinute * 2));
|
||||
assertEquals("1 hour, 0 minutes, 0 seconds", FormattingUtils.formatDurationAsWords(oneHour, true, true));
|
||||
assertEquals("1 hour, 0 minutes, 1 second", FormattingUtils.formatDurationAsWords(oneHour + oneSecond, true, true));
|
||||
assertEquals("1 hour, 0 minutes, 2 seconds", FormattingUtils.formatDurationAsWords(oneHour + oneSecond * 2, true, true));
|
||||
assertEquals("2 days, 21 hours, 28 minutes", FormattingUtils.formatDurationAsWords(oneDay * 2 + oneHour * 21 + oneMinute * 28));
|
||||
assertEquals("110 days", FormattingUtils.formatDurationAsWords(oneDay * 110, false, false));
|
||||
assertEquals("10 days, 10 hours, 10 minutes, 10 seconds", FormattingUtils.formatDurationAsWords(oneDay * 10 + oneHour * 10 + oneMinute * 10 + oneSecond * 10, true, false));
|
||||
assertEquals("1 hour, 2 seconds", FormattingUtils.formatDurationAsWords(oneHour + oneSecond * 2, true, false));
|
||||
assertEquals("1 hour", FormattingUtils.formatDurationAsWords(oneHour + oneSecond * 2, false, false));
|
||||
assertEquals("0 hours, 0 minutes, 1 second", FormattingUtils.formatDurationAsWords(oneSecond, true, true));
|
||||
assertEquals("1 second", FormattingUtils.formatDurationAsWords(oneSecond, true, false));
|
||||
assertEquals("0 hours", FormattingUtils.formatDurationAsWords(oneSecond, false, false));
|
||||
assertEquals("", FormattingUtils.formatDurationAsWords(0));
|
||||
assertTrue(FormattingUtils.formatDurationAsWords(0).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatPrice() {
|
||||
assertEquals("100.0000", FormattingUtils.formatPrice(make(usdPrice)));
|
||||
assertEquals("7098.4700", FormattingUtils.formatPrice(make(usdPrice.but(with(priceString, "7098.4700")))));
|
||||
}
|
||||
}
|
69
core/src/test/java/bisq/core/util/ProtoUtilTest.java
Normal file
69
core/src/test/java/bisq/core/util/ProtoUtilTest.java
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import bisq.core.offer.OpenOffer;
|
||||
|
||||
import bisq.common.proto.ProtoUtil;
|
||||
|
||||
import protobuf.OfferPayload;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@SuppressWarnings("UnusedAssignment")
|
||||
public class ProtoUtilTest {
|
||||
|
||||
//TODO Use NetworkProtoResolver, PersistenceProtoResolver or ProtoResolver which are all in bisq.common.
|
||||
@Test
|
||||
public void testEnum() {
|
||||
OfferPayload.Direction direction = OfferPayload.Direction.SELL;
|
||||
OfferPayload.Direction direction2 = OfferPayload.Direction.BUY;
|
||||
OfferPayload.Direction realDirection = getDirection(direction);
|
||||
OfferPayload.Direction realDirection2 = getDirection(direction2);
|
||||
assertEquals("SELL", realDirection.name());
|
||||
assertEquals("BUY", realDirection2.name());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnknownEnum() {
|
||||
protobuf.OpenOffer.State result = protobuf.OpenOffer.State.PB_ERROR;
|
||||
try {
|
||||
OpenOffer.State finalResult = OpenOffer.State.valueOf(result.name());
|
||||
fail();
|
||||
} catch (IllegalArgumentException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnknownEnumFix() {
|
||||
protobuf.OpenOffer.State result = protobuf.OpenOffer.State.PB_ERROR;
|
||||
try {
|
||||
OpenOffer.State finalResult = ProtoUtil.enumFromProto(OpenOffer.State.class, result.name());
|
||||
assertEquals(OpenOffer.State.AVAILABLE, ProtoUtil.enumFromProto(OpenOffer.State.class, "AVAILABLE"));
|
||||
} catch (IllegalArgumentException e) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
public static OfferPayload.Direction getDirection(OfferPayload.Direction direction) {
|
||||
return OfferPayload.Direction.valueOf(direction.name());
|
||||
}
|
||||
}
|
313
core/src/test/java/bisq/core/util/RegexValidatorTest.java
Normal file
313
core/src/test/java/bisq/core/util/RegexValidatorTest.java
Normal file
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import bisq.core.locale.GlobalSettings;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.util.validation.RegexValidator;
|
||||
import bisq.core.util.validation.RegexValidatorFactory;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
||||
public class RegexValidatorTest {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
Locale.setDefault(new Locale("en", "US"));
|
||||
GlobalSettings.setLocale(new Locale("en", "US"));
|
||||
Res.setBaseCurrencyCode("BTC");
|
||||
Res.setBaseCurrencyName("Bitcoin");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddressRegexValidator() {
|
||||
RegexValidator regexValidator = RegexValidatorFactory.addressRegexValidator();
|
||||
|
||||
assertTrue(regexValidator.validate("").isValid);
|
||||
assertFalse(regexValidator.validate(" ").isValid);
|
||||
|
||||
// onion V2 addresses
|
||||
assertTrue(regexValidator.validate("abcdefghij234567.onion").isValid);
|
||||
assertTrue(regexValidator.validate("abcdefghijklmnop.onion,abcdefghijklmnop.onion").isValid);
|
||||
assertTrue(regexValidator.validate("abcdefghijklmnop.onion, abcdefghijklmnop.onion").isValid);
|
||||
assertTrue(regexValidator.validate("qrstuvwxyzABCDEF.onion,qrstuvwxyzABCDEF.onion,aaaaaaaaaaaaaaaa.onion").isValid);
|
||||
assertTrue(regexValidator.validate("GHIJKLMNOPQRSTUV.onion:9999").isValid);
|
||||
assertTrue(regexValidator.validate("WXYZ234567abcdef.onion,GHIJKLMNOPQRSTUV.onion:9999").isValid);
|
||||
assertTrue(regexValidator.validate("aaaaaaaaaaaaaaaa.onion:9999,WXYZ234567abcdef.onion:9999,2222222222222222.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("abcd.onion").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghijklmnop,abcdefghijklmnop.onion").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghi2345689.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("onion:9999,abcdefghijklmnop.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghijklmnop.onion:").isValid);
|
||||
|
||||
// onion v3 addresses
|
||||
assertFalse(regexValidator.validate("32zzibxmqi2ybxpqyggwwuwz7a3lbvtzoloti7cxoevyvijexvgsfei.onion:8333").isValid); // 1 missing char
|
||||
assertTrue(regexValidator.validate("wizseedscybbttk4bmb2lzvbuk2jtect37lcpva4l3twktmkzemwbead.onion:8000").isValid);
|
||||
|
||||
// ipv4 addresses
|
||||
assertTrue(regexValidator.validate("12.34.56.78").isValid);
|
||||
assertTrue(regexValidator.validate("12.34.56.78,87.65.43.21").isValid);
|
||||
assertTrue(regexValidator.validate("12.34.56.78:8888").isValid);
|
||||
assertFalse(regexValidator.validate("12.34.56.788").isValid);
|
||||
assertFalse(regexValidator.validate("12.34.56.78:").isValid);
|
||||
|
||||
// ipv6 addresses
|
||||
assertTrue(regexValidator.validate("FE80:0000:0000:0000:0202:B3FF:FE1E:8329").isValid);
|
||||
assertTrue(regexValidator.validate("FE80::0202:B3FF:FE1E:8329").isValid);
|
||||
assertTrue(regexValidator.validate("FE80::0202:B3FF:FE1E:8329,FE80:0000:0000:0000:0202:B3FF:FE1E:8329").isValid);
|
||||
assertTrue(regexValidator.validate("::1").isValid);
|
||||
assertTrue(regexValidator.validate("fe80::").isValid);
|
||||
assertTrue(regexValidator.validate("2001::").isValid);
|
||||
assertTrue(regexValidator.validate("[::1]:8333").isValid);
|
||||
assertTrue(regexValidator.validate("[FE80::0202:B3FF:FE1E:8329]:8333").isValid);
|
||||
assertTrue(regexValidator.validate("[2001:db8::1]:80").isValid);
|
||||
assertTrue(regexValidator.validate("[aaaa::bbbb]:8333").isValid);
|
||||
assertFalse(regexValidator.validate("1200:0000:AB00:1234:O000:2552:7777:1313").isValid);
|
||||
|
||||
// fqdn addresses
|
||||
assertTrue(regexValidator.validate("example.com").isValid);
|
||||
assertTrue(regexValidator.validate("mynode.local:8333").isValid);
|
||||
assertTrue(regexValidator.validate("foo.example.com,bar.example.com").isValid);
|
||||
assertTrue(regexValidator.validate("foo.example.com:8333,bar.example.com:8333").isValid);
|
||||
|
||||
assertFalse(regexValidator.validate("mynode.local:65536").isValid);
|
||||
assertFalse(regexValidator.validate("-example.com").isValid);
|
||||
assertFalse(regexValidator.validate("example-.com").isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnionAddressRegexValidator() {
|
||||
RegexValidator regexValidator = RegexValidatorFactory.onionAddressRegexValidator();
|
||||
|
||||
assertTrue(regexValidator.validate("").isValid);
|
||||
assertFalse(regexValidator.validate(" ").isValid);
|
||||
|
||||
// onion V2 addresses
|
||||
assertTrue(regexValidator.validate("abcdefghij234567.onion").isValid);
|
||||
assertTrue(regexValidator.validate("abcdefghijklmnop.onion,abcdefghijklmnop.onion").isValid);
|
||||
assertTrue(regexValidator.validate("abcdefghijklmnop.onion, abcdefghijklmnop.onion").isValid);
|
||||
assertTrue(regexValidator.validate("qrstuvwxyzABCDEF.onion,qrstuvwxyzABCDEF.onion,aaaaaaaaaaaaaaaa.onion").isValid);
|
||||
assertTrue(regexValidator.validate("GHIJKLMNOPQRSTUV.onion:9999").isValid);
|
||||
assertTrue(regexValidator.validate("WXYZ234567abcdef.onion,GHIJKLMNOPQRSTUV.onion:9999").isValid);
|
||||
assertTrue(regexValidator.validate("aaaaaaaaaaaaaaaa.onion:9999,WXYZ234567abcdef.onion:9999,2222222222222222.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("abcd.onion").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghijklmnop,abcdefghijklmnop.onion").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghi2345689.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("onion:9999,abcdefghijklmnop.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghijklmnop.onion:").isValid);
|
||||
|
||||
// onion v3 addresses
|
||||
assertFalse(regexValidator.validate("32zzibxmqi2ybxpqyggwwuwz7a3lbvtzoloti7cxoevyvijexvgsfei.onion:8333").isValid); // 1 missing char
|
||||
assertTrue(regexValidator.validate("wizseedscybbttk4bmb2lzvbuk2jtect37lcpva4l3twktmkzemwbead.onion:8000").isValid);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalnetAddressRegexValidator() {
|
||||
RegexValidator regexValidator = RegexValidatorFactory.localnetAddressRegexValidator();
|
||||
|
||||
assertTrue(regexValidator.validate("").isValid);
|
||||
assertFalse(regexValidator.validate(" ").isValid);
|
||||
|
||||
// onion V2 addresses
|
||||
assertFalse(regexValidator.validate("abcdefghij234567.onion").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghijklmnop.onion,abcdefghijklmnop.onion").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghijklmnop.onion, abcdefghijklmnop.onion").isValid);
|
||||
assertFalse(regexValidator.validate("qrstuvwxyzABCDEF.onion,qrstuvwxyzABCDEF.onion,aaaaaaaaaaaaaaaa.onion").isValid);
|
||||
assertFalse(regexValidator.validate("GHIJKLMNOPQRSTUV.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("WXYZ234567abcdef.onion,GHIJKLMNOPQRSTUV.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("aaaaaaaaaaaaaaaa.onion:9999,WXYZ234567abcdef.onion:9999,2222222222222222.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("abcd.onion").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghijklmnop,abcdefghijklmnop.onion").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghi2345689.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("onion:9999,abcdefghijklmnop.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghijklmnop.onion:").isValid);
|
||||
|
||||
// onion v3 addresses
|
||||
assertFalse(regexValidator.validate("32zzibxmqi2ybxpqyggwwuwz7a3lbvtzoloti7cxoevyvijexvgsfei.onion:8333").isValid); // 1 missing char
|
||||
assertFalse(regexValidator.validate("wizseedscybbttk4bmb2lzvbuk2jtect37lcpva4l3twktmkzemwbead.onion:8000").isValid);
|
||||
|
||||
// ipv4 addresses
|
||||
assertFalse(regexValidator.validate("12.34.56.78").isValid);
|
||||
assertFalse(regexValidator.validate("12.34.56.78,87.65.43.21").isValid);
|
||||
assertFalse(regexValidator.validate("12.34.56.78:8888").isValid);
|
||||
assertFalse(regexValidator.validate("12.34.56.788").isValid);
|
||||
assertFalse(regexValidator.validate("12.34.56.78:").isValid);
|
||||
|
||||
// ipv4 local addresses
|
||||
assertTrue(regexValidator.validate("10.10.10.10").isValid);
|
||||
assertTrue(regexValidator.validate("172.19.1.1").isValid);
|
||||
assertTrue(regexValidator.validate("172.19.1.1").isValid);
|
||||
assertTrue(regexValidator.validate("192.168.1.1").isValid);
|
||||
assertTrue(regexValidator.validate("192.168.1.1,172.16.1.1").isValid);
|
||||
assertTrue(regexValidator.validate("192.168.1.1:8888,192.168.1.2:8888").isValid);
|
||||
assertFalse(regexValidator.validate("192.168.1.888").isValid);
|
||||
assertFalse(regexValidator.validate("192.168.1.1:").isValid);
|
||||
|
||||
// ipv4 autolocal addresses
|
||||
assertTrue(regexValidator.validate("169.254.123.232").isValid);
|
||||
|
||||
// ipv6 local addresses
|
||||
assertTrue(regexValidator.validate("fe80:2:3:4:5:6:7:8").isValid);
|
||||
assertTrue(regexValidator.validate("fe80::").isValid);
|
||||
assertTrue(regexValidator.validate("fc00::").isValid);
|
||||
assertTrue(regexValidator.validate("fd00::,fe80::1").isValid);
|
||||
assertTrue(regexValidator.validate("fd00::8").isValid);
|
||||
assertTrue(regexValidator.validate("fd00::7:8").isValid);
|
||||
assertTrue(regexValidator.validate("fd00::6:7:8").isValid);
|
||||
assertTrue(regexValidator.validate("fd00::5:6:7:8").isValid);
|
||||
assertTrue(regexValidator.validate("fd00::4:5:6:7:8").isValid);
|
||||
assertTrue(regexValidator.validate("fd00::3:4:5:6:7:8").isValid);
|
||||
assertTrue(regexValidator.validate("fd00:2:3:4:5:6:7:8").isValid);
|
||||
assertTrue(regexValidator.validate("fd00::0202:B3FF:FE1E:8329").isValid);
|
||||
assertTrue(regexValidator.validate("fd00::0202:B3FF:FE1E:8329,FE80::0202:B3FF:FE1E:8329").isValid);
|
||||
// ipv6 local with optional port at the end
|
||||
assertTrue(regexValidator.validate("[fd00::1]:8081").isValid);
|
||||
assertTrue(regexValidator.validate("[fd00::1]:8081,[fc00::1]:8081").isValid);
|
||||
assertTrue(regexValidator.validate("[FE80::0202:B3FF:FE1E:8329]:8333").isValid);
|
||||
|
||||
// ipv6 loopback
|
||||
assertFalse(regexValidator.validate("::1").isValid);
|
||||
|
||||
// ipv6 unicast
|
||||
assertFalse(regexValidator.validate("2001::").isValid);
|
||||
assertFalse(regexValidator.validate("[::1]:8333").isValid);
|
||||
assertFalse(regexValidator.validate("[2001:db8::1]:80").isValid);
|
||||
assertFalse(regexValidator.validate("[aaaa::bbbb]:8333").isValid);
|
||||
assertFalse(regexValidator.validate("1200:0000:AB00:1234:O000:2552:7777:1313").isValid);
|
||||
|
||||
// *.local fqdn hostnames
|
||||
assertTrue(regexValidator.validate("mynode.local").isValid);
|
||||
assertTrue(regexValidator.validate("mynode.local:8081").isValid);
|
||||
|
||||
// non-local fqdn hostnames
|
||||
assertFalse(regexValidator.validate("example.com").isValid);
|
||||
assertFalse(regexValidator.validate("foo.example.com,bar.example.com").isValid);
|
||||
assertFalse(regexValidator.validate("foo.example.com:8333,bar.example.com:8333").isValid);
|
||||
|
||||
// invalid fqdn hostnames
|
||||
assertFalse(regexValidator.validate("mynode.local:65536").isValid);
|
||||
assertFalse(regexValidator.validate("-example.com").isValid);
|
||||
assertFalse(regexValidator.validate("example-.com").isValid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalhostAddressRegexValidator() {
|
||||
RegexValidator regexValidator = RegexValidatorFactory.localhostAddressRegexValidator();
|
||||
|
||||
assertTrue(regexValidator.validate("").isValid);
|
||||
assertFalse(regexValidator.validate(" ").isValid);
|
||||
|
||||
// onion V2 addresses
|
||||
assertFalse(regexValidator.validate("abcdefghij234567.onion").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghijklmnop.onion,abcdefghijklmnop.onion").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghijklmnop.onion, abcdefghijklmnop.onion").isValid);
|
||||
assertFalse(regexValidator.validate("qrstuvwxyzABCDEF.onion,qrstuvwxyzABCDEF.onion,aaaaaaaaaaaaaaaa.onion").isValid);
|
||||
assertFalse(regexValidator.validate("GHIJKLMNOPQRSTUV.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("WXYZ234567abcdef.onion,GHIJKLMNOPQRSTUV.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("aaaaaaaaaaaaaaaa.onion:9999,WXYZ234567abcdef.onion:9999,2222222222222222.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("abcd.onion").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghijklmnop,abcdefghijklmnop.onion").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghi2345689.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("onion:9999,abcdefghijklmnop.onion:9999").isValid);
|
||||
assertFalse(regexValidator.validate("abcdefghijklmnop.onion:").isValid);
|
||||
|
||||
// onion v3 addresses
|
||||
assertFalse(regexValidator.validate("32zzibxmqi2ybxpqyggwwuwz7a3lbvtzoloti7cxoevyvijexvgsfei.onion:8333").isValid); // 1 missing char
|
||||
assertFalse(regexValidator.validate("wizseedscybbttk4bmb2lzvbuk2jtect37lcpva4l3twktmkzemwbead.onion:8000").isValid);
|
||||
|
||||
// ipv4 addresses
|
||||
assertFalse(regexValidator.validate("12.34.56.78").isValid);
|
||||
assertFalse(regexValidator.validate("12.34.56.78,87.65.43.21").isValid);
|
||||
assertFalse(regexValidator.validate("12.34.56.78:8888").isValid);
|
||||
assertFalse(regexValidator.validate("12.34.56.788").isValid);
|
||||
assertFalse(regexValidator.validate("12.34.56.78:").isValid);
|
||||
|
||||
// ipv4 loopback addresses
|
||||
assertTrue(regexValidator.validate("127.0.0.1").isValid);
|
||||
assertTrue(regexValidator.validate("127.0.1.1").isValid);
|
||||
|
||||
// ipv4 local addresses
|
||||
assertFalse(regexValidator.validate("10.10.10.10").isValid);
|
||||
assertFalse(regexValidator.validate("172.19.1.1").isValid);
|
||||
assertFalse(regexValidator.validate("172.19.1.1").isValid);
|
||||
assertFalse(regexValidator.validate("192.168.1.1").isValid);
|
||||
assertFalse(regexValidator.validate("192.168.1.1,172.16.1.1").isValid);
|
||||
assertFalse(regexValidator.validate("192.168.1.1:8888,192.168.1.2:8888").isValid);
|
||||
assertFalse(regexValidator.validate("192.168.1.888").isValid);
|
||||
assertFalse(regexValidator.validate("192.168.1.1:").isValid);
|
||||
|
||||
// ipv4 autolocal addresses
|
||||
assertFalse(regexValidator.validate("169.254.123.232").isValid);
|
||||
|
||||
// ipv6 local addresses
|
||||
assertFalse(regexValidator.validate("fe80::").isValid);
|
||||
assertFalse(regexValidator.validate("fc00::").isValid);
|
||||
assertFalse(regexValidator.validate("fd00::8").isValid);
|
||||
assertFalse(regexValidator.validate("fd00::7:8").isValid);
|
||||
assertFalse(regexValidator.validate("fd00::6:7:8").isValid);
|
||||
assertFalse(regexValidator.validate("fd00::5:6:7:8").isValid);
|
||||
assertFalse(regexValidator.validate("fd00::3:4:5:6:7:8").isValid);
|
||||
assertFalse(regexValidator.validate("fd00::4:5:6:7:8").isValid);
|
||||
assertFalse(regexValidator.validate("fd00:2:3:4:5:6:7:8").isValid);
|
||||
assertFalse(regexValidator.validate("fd00::0202:B3FF:FE1E:8329").isValid);
|
||||
|
||||
assertFalse(regexValidator.validate("FE80:0000:0000:0000:0202:B3FF:FE1E:8329").isValid);
|
||||
assertFalse(regexValidator.validate("FE80::0202:B3FF:FE1E:8329").isValid);
|
||||
assertFalse(regexValidator.validate("FE80::0202:B3FF:FE1E:8329,FE80:0000:0000:0000:0202:B3FF:FE1E:8329").isValid);
|
||||
// ipv6 local with optional port at the end
|
||||
assertFalse(regexValidator.validate("[fd00::1]:8081").isValid);
|
||||
assertFalse(regexValidator.validate("[fd00::1]:8081,[fc00::1]:8081").isValid);
|
||||
|
||||
// ipv6 loopback
|
||||
assertTrue(regexValidator.validate("::1").isValid);
|
||||
assertTrue(regexValidator.validate("::2").isValid);
|
||||
assertTrue(regexValidator.validate("[::1]:8333").isValid);
|
||||
|
||||
// ipv6 unicast
|
||||
assertFalse(regexValidator.validate("2001::").isValid);
|
||||
assertFalse(regexValidator.validate("[FE80::0202:B3FF:FE1E:8329]:8333").isValid);
|
||||
assertFalse(regexValidator.validate("[2001:db8::1]:80").isValid);
|
||||
assertFalse(regexValidator.validate("[aaaa::bbbb]:8333").isValid);
|
||||
assertFalse(regexValidator.validate("1200:0000:AB00:1234:O000:2552:7777:1313").isValid);
|
||||
|
||||
// localhost fqdn hostnames
|
||||
assertTrue(regexValidator.validate("localhost").isValid);
|
||||
assertTrue(regexValidator.validate("localhost:8081").isValid);
|
||||
|
||||
// local fqdn hostnames
|
||||
assertFalse(regexValidator.validate("mynode.local:8081").isValid);
|
||||
|
||||
// non-local fqdn hostnames
|
||||
assertFalse(regexValidator.validate("example.com").isValid);
|
||||
assertFalse(regexValidator.validate("foo.example.com,bar.example.com").isValid);
|
||||
assertFalse(regexValidator.validate("foo.example.com:8333,bar.example.com:8333").isValid);
|
||||
|
||||
// invalid fqdn hostnames
|
||||
assertFalse(regexValidator.validate("mynode.local:65536").isValid);
|
||||
assertFalse(regexValidator.validate("-example.com").isValid);
|
||||
assertFalse(regexValidator.validate("example-.com").isValid);
|
||||
}
|
||||
}
|
123
core/src/test/java/bisq/core/util/coin/CoinUtilTest.java
Normal file
123
core/src/test/java/bisq/core/util/coin/CoinUtilTest.java
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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.util.coin;
|
||||
|
||||
import bisq.core.monetary.Price;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class CoinUtilTest {
|
||||
|
||||
@Test
|
||||
public void testGetFeePerBtc() {
|
||||
assertEquals(Coin.parseCoin("1"), CoinUtil.getFeePerBtc(Coin.parseCoin("1"), Coin.parseCoin("1")));
|
||||
assertEquals(Coin.parseCoin("0.1"), CoinUtil.getFeePerBtc(Coin.parseCoin("0.1"), Coin.parseCoin("1")));
|
||||
assertEquals(Coin.parseCoin("0.01"), CoinUtil.getFeePerBtc(Coin.parseCoin("0.1"), Coin.parseCoin("0.1")));
|
||||
assertEquals(Coin.parseCoin("0.015"), CoinUtil.getFeePerBtc(Coin.parseCoin("0.3"), Coin.parseCoin("0.05")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinCoin() {
|
||||
assertEquals(Coin.parseCoin("1"), CoinUtil.minCoin(Coin.parseCoin("1"), Coin.parseCoin("1")));
|
||||
assertEquals(Coin.parseCoin("0.1"), CoinUtil.minCoin(Coin.parseCoin("0.1"), Coin.parseCoin("1")));
|
||||
assertEquals(Coin.parseCoin("0.01"), CoinUtil.minCoin(Coin.parseCoin("0.1"), Coin.parseCoin("0.01")));
|
||||
assertEquals(Coin.parseCoin("0"), CoinUtil.minCoin(Coin.parseCoin("0"), Coin.parseCoin("0.05")));
|
||||
assertEquals(Coin.parseCoin("0"), CoinUtil.minCoin(Coin.parseCoin("0.05"), Coin.parseCoin("0")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxCoin() {
|
||||
assertEquals(Coin.parseCoin("1"), CoinUtil.maxCoin(Coin.parseCoin("1"), Coin.parseCoin("1")));
|
||||
assertEquals(Coin.parseCoin("1"), CoinUtil.maxCoin(Coin.parseCoin("0.1"), Coin.parseCoin("1")));
|
||||
assertEquals(Coin.parseCoin("0.1"), CoinUtil.maxCoin(Coin.parseCoin("0.1"), Coin.parseCoin("0.01")));
|
||||
assertEquals(Coin.parseCoin("0.05"), CoinUtil.maxCoin(Coin.parseCoin("0"), Coin.parseCoin("0.05")));
|
||||
assertEquals(Coin.parseCoin("0.05"), CoinUtil.maxCoin(Coin.parseCoin("0.05"), Coin.parseCoin("0")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAdjustedAmount() {
|
||||
Coin result = CoinUtil.getAdjustedAmount(
|
||||
Coin.valueOf(100_000),
|
||||
Price.valueOf("USD", 1000_0000),
|
||||
20_000_000,
|
||||
1);
|
||||
assertEquals(
|
||||
"Minimum trade amount allowed should be adjusted to the smallest trade allowed.",
|
||||
"0.001 BTC",
|
||||
result.toFriendlyString()
|
||||
);
|
||||
|
||||
try {
|
||||
CoinUtil.getAdjustedAmount(
|
||||
Coin.ZERO,
|
||||
Price.valueOf("USD", 1000_0000),
|
||||
20_000_000,
|
||||
1);
|
||||
fail("Expected IllegalArgumentException to be thrown when amount is too low.");
|
||||
} catch (IllegalArgumentException iae) {
|
||||
assertEquals(
|
||||
"Unexpected exception message.",
|
||||
"amount needs to be above minimum of 10k satoshis",
|
||||
iae.getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
result = CoinUtil.getAdjustedAmount(
|
||||
Coin.valueOf(1_000_000),
|
||||
Price.valueOf("USD", 1000_0000),
|
||||
20_000_000,
|
||||
1);
|
||||
assertEquals(
|
||||
"Minimum allowed trade amount should not be adjusted.",
|
||||
"0.01 BTC",
|
||||
result.toFriendlyString()
|
||||
);
|
||||
|
||||
result = CoinUtil.getAdjustedAmount(
|
||||
Coin.valueOf(100_000),
|
||||
Price.valueOf("USD", 1000_0000),
|
||||
1_000_000,
|
||||
1);
|
||||
assertEquals(
|
||||
"Minimum trade amount allowed should respect maxTradeLimit and factor, if possible.",
|
||||
"0.001 BTC",
|
||||
result.toFriendlyString()
|
||||
);
|
||||
|
||||
// TODO(chirhonul): The following seems like it should raise an exception or otherwise fail.
|
||||
// We are asking for the smallest allowed BTC trade when price is 1000 USD each, and the
|
||||
// max trade limit is 5k sat = 0.00005 BTC. But the returned amount 0.00005 BTC, or
|
||||
// 0.05 USD worth, which is below the factor of 1 USD, but does respect the maxTradeLimit.
|
||||
// Basically the given constraints (maxTradeLimit vs factor) are impossible to both fulfill..
|
||||
result = CoinUtil.getAdjustedAmount(
|
||||
Coin.valueOf(100_000),
|
||||
Price.valueOf("USD", 1000_0000),
|
||||
5_000,
|
||||
1);
|
||||
assertEquals(
|
||||
"Minimum trade amount allowed with low maxTradeLimit should still respect that limit, even if result does not respect the factor specified.",
|
||||
"0.00005 BTC",
|
||||
result.toFriendlyString()
|
||||
);
|
||||
}
|
||||
}
|
57
core/src/test/resources/bisq/core/app/cli-output.txt
Normal file
57
core/src/test/resources/bisq/core/app/cli-output.txt
Normal file
|
@ -0,0 +1,57 @@
|
|||
Bisq Test version 0.1.0
|
||||
|
||||
Usage: bisq-test [options]
|
||||
|
||||
Options:
|
||||
|
||||
--name=<String> (default: Bisq)
|
||||
The name of the Bisq node
|
||||
|
||||
--another-option=<String> (default: WAT)
|
||||
This is a long description which will need to break over multiple
|
||||
linessssssssssss such that no line is longer than 80 characters in the
|
||||
help output.
|
||||
|
||||
--exactly-72-char-description=<String>
|
||||
012345678911234567892123456789312345678941234567895123456789612345678971
|
||||
|
||||
--exactly-72-char-description-with-spaces=<String>
|
||||
123456789 123456789 123456789 123456789 123456789 123456789 123456789 1
|
||||
|
||||
--90-char-description-without-spaces=<String>
|
||||
-123456789-223456789-323456789-423456789-523456789-623456789-723456789-823456789-923456789
|
||||
|
||||
--90-char-description-with-space-at-char-80=<String>
|
||||
-123456789-223456789-323456789-423456789-523456789-623456789-723456789-823456789
|
||||
923456789
|
||||
|
||||
--90-char-description-with-spaces-at-chars-5-and-80=<String>
|
||||
-123
|
||||
56789-223456789-323456789-423456789-523456789-623456789-723456789-823456789
|
||||
923456789
|
||||
|
||||
--90-char-description-with-space-at-char-73=<String>
|
||||
-123456789-223456789-323456789-423456789-523456789-623456789-723456789-8
|
||||
3456789-923456789
|
||||
|
||||
--1-char-description-with-only-a-space=<String>
|
||||
|
||||
--empty-description=<String>
|
||||
|
||||
--no-description=<String>
|
||||
|
||||
--no-arg
|
||||
Some description
|
||||
|
||||
--optional-arg=<value>
|
||||
Option description
|
||||
|
||||
--with-default-value=<String> (default: Wat)
|
||||
Some option with a default value
|
||||
|
||||
--data-dir=<File> (default: /Users/cbeams/Library/Application Support/Bisq)
|
||||
Application data directory
|
||||
|
||||
--enum-opt=<foo|bar|baz> (default: foo)
|
||||
Some option that accepts an enum value as an argument
|
||||
|
57
core/src/test/resources/bisq/core/app/cli-output_windows.txt
Normal file
57
core/src/test/resources/bisq/core/app/cli-output_windows.txt
Normal file
|
@ -0,0 +1,57 @@
|
|||
Bisq Test version 0.1.0
|
||||
|
||||
Usage: bisq-test [options]
|
||||
|
||||
Options:
|
||||
|
||||
--name=<String> (default: Bisq)
|
||||
The name of the Bisq node
|
||||
|
||||
--another-option=<String> (default: WAT)
|
||||
This is a long description which will need to break over multiple
|
||||
linessssssssssss such that no line is longer than 80 characters in the
|
||||
help output.
|
||||
|
||||
--exactly-72-char-description=<String>
|
||||
012345678911234567892123456789312345678941234567895123456789612345678971
|
||||
|
||||
--exactly-72-char-description-with-spaces=<String>
|
||||
123456789 123456789 123456789 123456789 123456789 123456789 123456789 1
|
||||
|
||||
--90-char-description-without-spaces=<String>
|
||||
-123456789-223456789-323456789-423456789-523456789-623456789-723456789-823456789-923456789
|
||||
|
||||
--90-char-description-with-space-at-char-80=<String>
|
||||
-123456789-223456789-323456789-423456789-523456789-623456789-723456789-823456789
|
||||
923456789
|
||||
|
||||
--90-char-description-with-spaces-at-chars-5-and-80=<String>
|
||||
-123
|
||||
56789-223456789-323456789-423456789-523456789-623456789-723456789-823456789
|
||||
923456789
|
||||
|
||||
--90-char-description-with-space-at-char-73=<String>
|
||||
-123456789-223456789-323456789-423456789-523456789-623456789-723456789-8
|
||||
3456789-923456789
|
||||
|
||||
--1-char-description-with-only-a-space=<String>
|
||||
|
||||
--empty-description=<String>
|
||||
|
||||
--no-description=<String>
|
||||
|
||||
--no-arg
|
||||
Some description
|
||||
|
||||
--optional-arg=<value>
|
||||
Option description
|
||||
|
||||
--with-default-value=<String> (default: Wat)
|
||||
Some option with a default value
|
||||
|
||||
--data-dir=<File> (default: \Users\cbeams\Library\Application Support\Bisq)
|
||||
Application data directory
|
||||
|
||||
--enum-opt=<foo|bar|baz> (default: foo)
|
||||
Some option that accepts an enum value as an argument
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
00000020a33d8cf2a1567a148dad1a4099599bafa631135262413a4bdd1182be5673471abe69039afc7c93936e3e2860da8cab522281fe4139a453d1
|
||||
1a3d5cfe75e93761bf25b25fffff7f20010000000b020000000001010000000000000000000000000000000000000000000000000000000000000000
|
||||
ffffffff05028b000101ffffffff02495b062a0100000017a914f2d479a78b981e4a2b05be5f89ef7c468ac48e78870000000000000000266a24aa21
|
||||
a9ed9f5a62816cebb2044be9db74a26762020defe9e8b251b73af2abefad7d4b355c0120000000000000000000000000000000000000000000000000
|
||||
0000000000000000000000000100000001274c7174e814eb731712b80187660f7a6ab2949a3271ef80037685cdde2d78c3020000006b483045022100
|
||||
b6c2fa10587d6fed3a0eecfd098b160f69a850beca139fe03ef65bec4cba1c5b02204a833a16c22bbd32722243ea3270e672f646ee9406e8797e1109
|
||||
3951e92efbd5012103dcca91c2ec7229f1b4f4c4f664c92d3303dddef8d38736f6a7f28de16f3ce416ffffffff03881300000000000017a9144c0e48
|
||||
93237f85479f489b32c8ff0faf3ee2e1c987c247090000000000160014007128282856f8e8f3c75909f9f1474b55cb1f1605f8902500000000160014
|
||||
9bc809698674ec7c01d35d438e9d0de1aa87b6c800000000010000000114facc8cf47a984cedf9ba84db10ad767e18c6fb6edbac39ce8f138a1e5b43
|
||||
9100000000da0047304402201f00d9a4aab1a3a239f1ad95a910092c0c55423480d609eaad4599cf7ecb7f480220668b1a9cf5624b1c4ece6da3f64b
|
||||
c6021e509f588ae1006601acd8a9f83b357601483045022100982eca77a72a2bdba51b9231afd4521400bee1bb7830634eb26db2b0c621bc46022073
|
||||
d7325916e2b5ceb1d2e510a5161fd9115105a8dafa94068864624bb10d190e014752210229713ad5c604c585128b3a5da6de20d78fc33bd3b595e999
|
||||
1f4c0e1fee99f845210398ad45a74bf5a5c5a8ec31de6815d2e805a23e68c0f8001770e74bc4c17c5b3152aefeffffff01b06a21000000000017a914
|
||||
4c0e4893237f85479f489b32c8ff0faf3ee2e1c9878a0000000100000000010133ba0d88494567a02bffc406b31bd5eb29f0b536a96326baca349b3a
|
||||
96f241e90200000000ffffffff03881300000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987067e180000000000160014f6da24
|
||||
d081a1b63dcacf4a5762a8ed91fd472c685b74b900000000001600144755561caa18d651bf59912545764811d0ab96f60247304402200d4f21475675
|
||||
3861adf835f5d175835d3cd50a19edbf9881572ab7f831a030de0220181b94bbb7d7b0a7a6eee06881a3265cb28405f2d14055e1ff23ac6500471930
|
||||
012102772467db1e5909e7e9f2b169daccf556e7e2981b145c756db649f3972d913c320000000001000000000101e06ec4548803dadb10ef6c66e4f3
|
||||
1f319161dc9ec31631e967773b8e042836180200000000ffffffff03881300000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987
|
||||
f4390900000000001600145230c895305a232ef2a4feb0a91e7d99e22fd515d20bd20000000000160014b6fbbd9053e47891fae7f3db7dd3966062b2
|
||||
513c0247304402203eeb1713b582be1d74bf6a9f95c573dd41baeedf1efd1bc9a1ad1cccad97c4f70220799a399f53f9325f6cf9681b0138f80bd80f
|
||||
3dc900a4d0ab5cc3c97d5be85f1801210255f56a7be9f88ccf5885ac2f8cd67320424d533d71083270a2891b2488ffb22b0000000001000000000101
|
||||
4c701c32d3b0ce9408d8ec96d80934dbc6cb42df616e6e751dae82afdb46214e0200000000ffffffff03881300000000000017a9144c0e4893237f85
|
||||
479f489b32c8ff0faf3ee2e1c987c02709000000000016001489c79bc0628d2d8b1cd91c2ed0e75db13e6f3f3a8a07a00600000000160014062d20a6
|
||||
692350b7a39397c50857a7f725788da002483045022100ebb8e0ddab46b762e3a9555442cc7ee35c4353d9152e856c97251913902a5056022010d3a0
|
||||
bb51d931a18174dc8ed0ffa37c5ff29f8e924b71d86850de31f3ea4c6e012102e0284cdeae6a8c971e2ea5004ebf9196ee9b3037d6f1ed039c4b5672
|
||||
a69cddc60000000001000000000101c3f609b5166e32bd0c29168767621bfc56411a3ff9e84932fc2c612407566a370200000000ffffffff03881300
|
||||
000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987006a1800000000001600141437b91493d1929b1b42a80e83229c347c28f937
|
||||
eaaf90060000000016001491ad2cce99e8e4455de5f44559816b98213f3503024830450221008f8eee212f209ba2a179197bd4ba35a35ad7a3045990
|
||||
25ddbd192a6e7e64c1920220242c297726948ad408ce54c9a0e0287b283c53dc68323537f24a7e3ecd8c526b012103f870bcd3a46e80e4b1236302e6
|
||||
2318b412cc97ef096fc976a89deb569dc11ef1000000000100000001ba4f0ae59d7cb81f0c7edd63796387fde6825a3536953c2824d7d945c192bd20
|
||||
020000006b483045022100ba97f6336b3bb3e07cf584010c7b8ab52957e34e462e71252c63f498d51f45b70220708dd78d2d9943f8c176055963ab70
|
||||
6870274068fe7b1e5d87592a837336a5340121039978f14b2463d7d4790cdf2a37c2a3d872dd517ca91db7f6f7a858a7ac661c60ffffffff03881300
|
||||
000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987c02709000000000016001413afc3a26c010dddaf2410a9d97b5054a4e8d309
|
||||
04e05938000000001600145228ee46a95383b314396dda75e707b8bed830340000000001000000015d71dc04e56bc08b61180a2b9531a0747f56615f
|
||||
f27ec51cbc72ac7a800cc27a020000006a473044022073a8a8ee9cc490093e6de5708b3727cef35f41038713fab9a5c235b4b400b73102206f97f4fc
|
||||
8faefb534e85c0f3773f2d67781c9871f681211276619054fc54015201210374e07f24beca2270cf305100652149a64c80d76611f775ec276658aeae
|
||||
4ef0b5ffffffff03881300000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987006a1800000000001600141c397ba7ea8410dbd5
|
||||
56c51f8184a14ab015114324b6a10600000000160014c38c3d890c415f2c13f6925c1ad1d4a7cb4f7dfe0000000001000000000102142a200a7460ad
|
||||
f754232337f48bf7c38ba411eed0cd2f98900ffd9f2adc1ed20100000000ffffffff99d67c2a69b04831bd229ea6022c91c68999c38d0ba92211b56f
|
||||
15c5f8222bf50100000000ffffffff03e803000000000000220020223d978073802f79e6ecdc7591e5dc1f0ea7030d6466f73c6b90391bc72e886f00
|
||||
00000000000000226a20758e9207848c631c6839b1382bb22a52b6ef0645d733389d7be2efb1e8b71454db972100000000001600145e41d2fb8de1c2
|
||||
50410416d8dd153c685d3f9c7b024730440220073a37eb4371dc3d0cf218d6e9b8e6044275acd07402ccebdf24f65b60a3c1f70220647e71c173f992
|
||||
fc0c5ec6c2b0b1653a95118098269e318c41d1bc33da3ff14f012103c3e858472f39d31c6defdf38b4778660501f0ccfa524b3dd8ba61117b7646635
|
||||
0247304402202b8ef5de1c56328d3797265272540a054fc04c158b23ee6385b69b14486422c10220749f591fcf4ef995df8f1e7d9aa3cf0c045f1616
|
||||
386b86b419197b360c871fca012102c73e60f00bc72b56568a9f371b9122b3ee29d41730670e98ff8da58e7bbfab280000000001000000000102913a
|
||||
5817e0b3bddb0bc6869e12200b5115be11522cf76125229d47e184da48460100000000ffffffff684d66517d21106d1bcbba96964e31a532fec33c09
|
||||
8cd621e2500c702340b6780100000000ffffffff03e803000000000000220020e3e81046fd9659b5725736efa404bc1c8f9b2ff6f0af7cb7ddacdcc6
|
||||
1e1c72310000000000000000226a20c63aacf2e8be20752b6f689c0308967cbc335641f2948a4a7962fdde6c464730f2962100000000001600145e41
|
||||
d2fb8de1c250410416d8dd153c685d3f9c7b02483045022100c9665b9abe7fcab10f775eeafc2391c1fee84c50b50df6d697b1db9a7eea5dd3022070
|
||||
cb7aa57b8f5bf9f2eff11263cf2ea871ab7b9ddfc8e47671cee50ada547243012102016f9a6cc454bd1e74c28df36a079231a215812c60581d1e1745
|
||||
e650f82bd1230248304502210094cdec8e08f32919b3f25c8672041305c848b4206256ad64f7090dc97dfd1bf002205c397a310cebc690fac04d1394
|
||||
5e012d0031246d4574e92f97e3701ca729b6140121029b739486d7cf402b3db3913187fad7897e8a5ec3cd8607e9b5fc54a71958b03100000000
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"hash": "015f37a20d517645a11a6cdd316049f41bc77b4a4057b2dd092114b78147f42c",
|
||||
"confirmations": 12,
|
||||
"strippedsize": 2270,
|
||||
"size": 3178,
|
||||
"weight": 9988,
|
||||
"height": 139,
|
||||
"version": 536870912,
|
||||
"versionHex": "20000000",
|
||||
"merkleroot": "6137e975fe5c3d1ad153a43941fe812252ab8cda60283e6e93937cfc9a0369be",
|
||||
"tx": [
|
||||
"09bbfd286d0399b57ac7c85956fccbe7e080cedd7a01723d8d68038f0cf57159",
|
||||
"e2fc769668f7306ca09865c10dd744ed5510f1cec35b8f854c9ed346229a303b",
|
||||
"b36a2c90dab09a0b99d30a3b132af37b79d8266a1510decc34683a2784228337",
|
||||
"f52b22f8c5156fb51122a90b8dc39989c6912c02a69e22bd3148b0692a7cd699",
|
||||
"4648da84e1479d222561f72c5211be15510b20129e86c60bdbbdb3e017583a91",
|
||||
"d21edc2a9ffd0f90982fcdd0ee11a48bc3f78bf437232354f7ad60740a202a14",
|
||||
"78b64023700c50e221d68c093cc3fe32a5314e9696bacb1b6d10217d51664d68",
|
||||
"dd4243c2743a2d2351814a14628e1976e6fb208e63bd2bbd441180c441205027",
|
||||
"aa33deb87512c2c417a0a9a17c58a36dcf2686fca8b50cc608ad952178a350c2",
|
||||
"719606705c6832f7180ab9db5e1ce6a51bad80a5cbf3b57b806dd10f3d7d5124",
|
||||
"16cd3283068d2965f26045e15a285e39a762af32ec388ae8c28fb6cb2c468768"
|
||||
],
|
||||
"time": 1605510591,
|
||||
"mediantime": 1589548514,
|
||||
"nonce": 1,
|
||||
"bits": "207fffff",
|
||||
"difficulty": 4.656542373906925E-10,
|
||||
"chainwork": "0000000000000000000000000000000000000000000000000000000000000118",
|
||||
"nTx": 11,
|
||||
"previousblockhash": "1a477356be8211dd4b3a4162521331a6af9b5999401aad8d147a56a1f28c3da3",
|
||||
"nextblockhash": "7d8267341a57f1f626b450eb22b2dbf208f13ec176e1cc015aa4d2b2ea55016d"
|
||||
}
|
|
@ -0,0 +1,698 @@
|
|||
{
|
||||
"hash": "015f37a20d517645a11a6cdd316049f41bc77b4a4057b2dd092114b78147f42c",
|
||||
"confirmations": 12,
|
||||
"strippedsize": 2270,
|
||||
"size": 3178,
|
||||
"weight": 9988,
|
||||
"height": 139,
|
||||
"version": 536870912,
|
||||
"versionHex": "20000000",
|
||||
"merkleroot": "6137e975fe5c3d1ad153a43941fe812252ab8cda60283e6e93937cfc9a0369be",
|
||||
"tx": [
|
||||
{
|
||||
"txid": "09bbfd286d0399b57ac7c85956fccbe7e080cedd7a01723d8d68038f0cf57159",
|
||||
"hash": "ed1620cdd028c99d57ee0709f963976bb75cac93f5171f5a2f5dd9976e5c56bc",
|
||||
"version": 2,
|
||||
"size": 171,
|
||||
"vsize": 144,
|
||||
"weight": 576,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"coinbase": "028b000101",
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 50.00026953,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_HASH160 f2d479a78b981e4a2b05be5f89ef7c468ac48e78 OP_EQUAL",
|
||||
"hex": "a914f2d479a78b981e4a2b05be5f89ef7c468ac48e7887",
|
||||
"reqSigs": 1,
|
||||
"type": "scripthash",
|
||||
"addresses": [
|
||||
"2NFPC3k5RjZ4GAQgLqJdiVdJ6gqWqEBryXq"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.0,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_RETURN aa21a9ed9f5a62816cebb2044be9db74a26762020defe9e8b251b73af2abefad7d4b355c",
|
||||
"hex": "6a24aa21a9ed9f5a62816cebb2044be9db74a26762020defe9e8b251b73af2abefad7d4b355c",
|
||||
"type": "nulldata"
|
||||
}
|
||||
}
|
||||
],
|
||||
"hex": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff05028b000101ffffffff02495b062a0100000017a914f2d479a78b981e4a2b05be5f89ef7c468ac48e78870000000000000000266a24aa21a9ed9f5a62816cebb2044be9db74a26762020defe9e8b251b73af2abefad7d4b355c0120000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
},
|
||||
{
|
||||
"txid": "e2fc769668f7306ca09865c10dd744ed5510f1cec35b8f854c9ed346229a303b",
|
||||
"hash": "e2fc769668f7306ca09865c10dd744ed5510f1cec35b8f854c9ed346229a303b",
|
||||
"version": 1,
|
||||
"size": 252,
|
||||
"vsize": 252,
|
||||
"weight": 1008,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "c3782ddecd85760380ef71329a94b26a7a0f668701b8121773eb14e874714c27",
|
||||
"vout": 2,
|
||||
"scriptSig": {
|
||||
"asm": "3045022100b6c2fa10587d6fed3a0eecfd098b160f69a850beca139fe03ef65bec4cba1c5b02204a833a16c22bbd32722243ea3270e672f646ee9406e8797e11093951e92efbd5[ALL] 03dcca91c2ec7229f1b4f4c4f664c92d3303dddef8d38736f6a7f28de16f3ce416",
|
||||
"hex": "483045022100b6c2fa10587d6fed3a0eecfd098b160f69a850beca139fe03ef65bec4cba1c5b02204a833a16c22bbd32722243ea3270e672f646ee9406e8797e11093951e92efbd5012103dcca91c2ec7229f1b4f4c4f664c92d3303dddef8d38736f6a7f28de16f3ce416"
|
||||
},
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 5.0E-5,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_HASH160 4c0e4893237f85479f489b32c8ff0faf3ee2e1c9 OP_EQUAL",
|
||||
"hex": "a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987",
|
||||
"reqSigs": 1,
|
||||
"type": "scripthash",
|
||||
"addresses": [
|
||||
"2MzBNTJDjjXgViKBGnatDU3yWkJ8pJkEg9w"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.00608194,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 007128282856f8e8f3c75909f9f1474b55cb1f16",
|
||||
"hex": "0014007128282856f8e8f3c75909f9f1474b55cb1f16",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1qqpcjs2pg2muw3u78tyylnu28fd2uk8ckr30ezc"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 6.30257669,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 9bc809698674ec7c01d35d438e9d0de1aa87b6c8",
|
||||
"hex": "00149bc809698674ec7c01d35d438e9d0de1aa87b6c8",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1qn0yqj6vxwnk8cqwnt4pca8gdux4g0dkghc9rur"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"hex": "0100000001274c7174e814eb731712b80187660f7a6ab2949a3271ef80037685cdde2d78c3020000006b483045022100b6c2fa10587d6fed3a0eecfd098b160f69a850beca139fe03ef65bec4cba1c5b02204a833a16c22bbd32722243ea3270e672f646ee9406e8797e11093951e92efbd5012103dcca91c2ec7229f1b4f4c4f664c92d3303dddef8d38736f6a7f28de16f3ce416ffffffff03881300000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987c247090000000000160014007128282856f8e8f3c75909f9f1474b55cb1f1605f89025000000001600149bc809698674ec7c01d35d438e9d0de1aa87b6c800000000"
|
||||
},
|
||||
{
|
||||
"txid": "b36a2c90dab09a0b99d30a3b132af37b79d8266a1510decc34683a2784228337",
|
||||
"hash": "b36a2c90dab09a0b99d30a3b132af37b79d8266a1510decc34683a2784228337",
|
||||
"version": 1,
|
||||
"size": 301,
|
||||
"vsize": 301,
|
||||
"weight": 1204,
|
||||
"locktime": 138,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "91435b1e8a138fce39acdb6efbc6187e76ad10db84baf9ed4c987af48cccfa14",
|
||||
"vout": 0,
|
||||
"scriptSig": {
|
||||
"asm": "0 304402201f00d9a4aab1a3a239f1ad95a910092c0c55423480d609eaad4599cf7ecb7f480220668b1a9cf5624b1c4ece6da3f64bc6021e509f588ae1006601acd8a9f83b3576[ALL] 3045022100982eca77a72a2bdba51b9231afd4521400bee1bb7830634eb26db2b0c621bc46022073d7325916e2b5ceb1d2e510a5161fd9115105a8dafa94068864624bb10d190e[ALL] 52210229713ad5c604c585128b3a5da6de20d78fc33bd3b595e9991f4c0e1fee99f845210398ad45a74bf5a5c5a8ec31de6815d2e805a23e68c0f8001770e74bc4c17c5b3152ae",
|
||||
"hex": "0047304402201f00d9a4aab1a3a239f1ad95a910092c0c55423480d609eaad4599cf7ecb7f480220668b1a9cf5624b1c4ece6da3f64bc6021e509f588ae1006601acd8a9f83b357601483045022100982eca77a72a2bdba51b9231afd4521400bee1bb7830634eb26db2b0c621bc46022073d7325916e2b5ceb1d2e510a5161fd9115105a8dafa94068864624bb10d190e014752210229713ad5c604c585128b3a5da6de20d78fc33bd3b595e9991f4c0e1fee99f845210398ad45a74bf5a5c5a8ec31de6815d2e805a23e68c0f8001770e74bc4c17c5b3152ae"
|
||||
},
|
||||
"sequence": 4294967294
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 0.0219,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_HASH160 4c0e4893237f85479f489b32c8ff0faf3ee2e1c9 OP_EQUAL",
|
||||
"hex": "a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987",
|
||||
"reqSigs": 1,
|
||||
"type": "scripthash",
|
||||
"addresses": [
|
||||
"2MzBNTJDjjXgViKBGnatDU3yWkJ8pJkEg9w"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"hex": "010000000114facc8cf47a984cedf9ba84db10ad767e18c6fb6edbac39ce8f138a1e5b439100000000da0047304402201f00d9a4aab1a3a239f1ad95a910092c0c55423480d609eaad4599cf7ecb7f480220668b1a9cf5624b1c4ece6da3f64bc6021e509f588ae1006601acd8a9f83b357601483045022100982eca77a72a2bdba51b9231afd4521400bee1bb7830634eb26db2b0c621bc46022073d7325916e2b5ceb1d2e510a5161fd9115105a8dafa94068864624bb10d190e014752210229713ad5c604c585128b3a5da6de20d78fc33bd3b595e9991f4c0e1fee99f845210398ad45a74bf5a5c5a8ec31de6815d2e805a23e68c0f8001770e74bc4c17c5b3152aefeffffff01b06a21000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c9878a000000"
|
||||
},
|
||||
{
|
||||
"txid": "f52b22f8c5156fb51122a90b8dc39989c6912c02a69e22bd3148b0692a7cd699",
|
||||
"hash": "a1cb920222f6470dae2b90af42ec4da2e541bc4de9cec11441ca2dd0032bca2c",
|
||||
"version": 1,
|
||||
"size": 254,
|
||||
"vsize": 173,
|
||||
"weight": 689,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "e941f2963a9b34caba2663a936b5f029ebd51bb306c4ff2ba0674549880dba33",
|
||||
"vout": 2,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"txinwitness": [
|
||||
"304402200d4f214756753861adf835f5d175835d3cd50a19edbf9881572ab7f831a030de0220181b94bbb7d7b0a7a6eee06881a3265cb28405f2d14055e1ff23ac650047193001",
|
||||
"02772467db1e5909e7e9f2b169daccf556e7e2981b145c756db649f3972d913c32"
|
||||
],
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 5.0E-5,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_HASH160 4c0e4893237f85479f489b32c8ff0faf3ee2e1c9 OP_EQUAL",
|
||||
"hex": "a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987",
|
||||
"reqSigs": 1,
|
||||
"type": "scripthash",
|
||||
"addresses": [
|
||||
"2MzBNTJDjjXgViKBGnatDU3yWkJ8pJkEg9w"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.01605126,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 f6da24d081a1b63dcacf4a5762a8ed91fd472c68",
|
||||
"hex": "0014f6da24d081a1b63dcacf4a5762a8ed91fd472c68",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1q7mdzf5yp5xmrmjk0fftk928dj875wtrgkwqr5x"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.12153947,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 4755561caa18d651bf59912545764811d0ab96f6",
|
||||
"hex": "00144755561caa18d651bf59912545764811d0ab96f6",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1qga24v892rrt9r06ejyj52ajgz8g2h9hkwquzg4"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"hex": "0100000000010133ba0d88494567a02bffc406b31bd5eb29f0b536a96326baca349b3a96f241e90200000000ffffffff03881300000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987067e180000000000160014f6da24d081a1b63dcacf4a5762a8ed91fd472c685b74b900000000001600144755561caa18d651bf59912545764811d0ab96f60247304402200d4f214756753861adf835f5d175835d3cd50a19edbf9881572ab7f831a030de0220181b94bbb7d7b0a7a6eee06881a3265cb28405f2d14055e1ff23ac6500471930012102772467db1e5909e7e9f2b169daccf556e7e2981b145c756db649f3972d913c3200000000"
|
||||
},
|
||||
{
|
||||
"txid": "4648da84e1479d222561f72c5211be15510b20129e86c60bdbbdb3e017583a91",
|
||||
"hash": "16e1ae45d2c6ddaaefeed97a822a95a2933096bf377db147a63d9b730cc30d20",
|
||||
"version": 1,
|
||||
"size": 254,
|
||||
"vsize": 173,
|
||||
"weight": 689,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "183628048e3b7767e93116c39edc6191311ff3e4666cef10dbda038854c46ee0",
|
||||
"vout": 2,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"txinwitness": [
|
||||
"304402203eeb1713b582be1d74bf6a9f95c573dd41baeedf1efd1bc9a1ad1cccad97c4f70220799a399f53f9325f6cf9681b0138f80bd80f3dc900a4d0ab5cc3c97d5be85f1801",
|
||||
"0255f56a7be9f88ccf5885ac2f8cd67320424d533d71083270a2891b2488ffb22b"
|
||||
],
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 5.0E-5,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_HASH160 4c0e4893237f85479f489b32c8ff0faf3ee2e1c9 OP_EQUAL",
|
||||
"hex": "a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987",
|
||||
"reqSigs": 1,
|
||||
"type": "scripthash",
|
||||
"addresses": [
|
||||
"2MzBNTJDjjXgViKBGnatDU3yWkJ8pJkEg9w"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.0060466,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 5230c895305a232ef2a4feb0a91e7d99e22fd515",
|
||||
"hex": "00145230c895305a232ef2a4feb0a91e7d99e22fd515",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1q2gcv39fstg3jau4yl6c2j8nan83zl4g4w5ch6j"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.13765586,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 b6fbbd9053e47891fae7f3db7dd3966062b2513c",
|
||||
"hex": "0014b6fbbd9053e47891fae7f3db7dd3966062b2513c",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1qkmammyznu3ufr7h870dhm5ukvp3ty5fudkl63e"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"hex": "01000000000101e06ec4548803dadb10ef6c66e4f31f319161dc9ec31631e967773b8e042836180200000000ffffffff03881300000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987f4390900000000001600145230c895305a232ef2a4feb0a91e7d99e22fd515d20bd20000000000160014b6fbbd9053e47891fae7f3db7dd3966062b2513c0247304402203eeb1713b582be1d74bf6a9f95c573dd41baeedf1efd1bc9a1ad1cccad97c4f70220799a399f53f9325f6cf9681b0138f80bd80f3dc900a4d0ab5cc3c97d5be85f1801210255f56a7be9f88ccf5885ac2f8cd67320424d533d71083270a2891b2488ffb22b00000000"
|
||||
},
|
||||
{
|
||||
"txid": "d21edc2a9ffd0f90982fcdd0ee11a48bc3f78bf437232354f7ad60740a202a14",
|
||||
"hash": "bae1180680abb989f5b89b5f49729d3eedcf757a12fd0b7649c944a4ba37eaee",
|
||||
"version": 1,
|
||||
"size": 255,
|
||||
"vsize": 173,
|
||||
"weight": 690,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "4e2146dbaf82ae1d756e6e61df42cbc6db3409d896ecd80894ceb0d3321c704c",
|
||||
"vout": 2,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"txinwitness": [
|
||||
"3045022100ebb8e0ddab46b762e3a9555442cc7ee35c4353d9152e856c97251913902a5056022010d3a0bb51d931a18174dc8ed0ffa37c5ff29f8e924b71d86850de31f3ea4c6e01",
|
||||
"02e0284cdeae6a8c971e2ea5004ebf9196ee9b3037d6f1ed039c4b5672a69cddc6"
|
||||
],
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 5.0E-5,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_HASH160 4c0e4893237f85479f489b32c8ff0faf3ee2e1c9 OP_EQUAL",
|
||||
"hex": "a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987",
|
||||
"reqSigs": 1,
|
||||
"type": "scripthash",
|
||||
"addresses": [
|
||||
"2MzBNTJDjjXgViKBGnatDU3yWkJ8pJkEg9w"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.006,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 89c79bc0628d2d8b1cd91c2ed0e75db13e6f3f3a",
|
||||
"hex": "001489c79bc0628d2d8b1cd91c2ed0e75db13e6f3f3a",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1q38rehsrz35kck8xershdpe6akylx70e63azh0p"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 1.11150986,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 062d20a6692350b7a39397c50857a7f725788da0",
|
||||
"hex": "0014062d20a6692350b7a39397c50857a7f725788da0",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1qqckjpfnfydgt0gunjlzss4a87ujh3rdq7u4yrr"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"hex": "010000000001014c701c32d3b0ce9408d8ec96d80934dbc6cb42df616e6e751dae82afdb46214e0200000000ffffffff03881300000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987c02709000000000016001489c79bc0628d2d8b1cd91c2ed0e75db13e6f3f3a8a07a00600000000160014062d20a6692350b7a39397c50857a7f725788da002483045022100ebb8e0ddab46b762e3a9555442cc7ee35c4353d9152e856c97251913902a5056022010d3a0bb51d931a18174dc8ed0ffa37c5ff29f8e924b71d86850de31f3ea4c6e012102e0284cdeae6a8c971e2ea5004ebf9196ee9b3037d6f1ed039c4b5672a69cddc600000000"
|
||||
},
|
||||
{
|
||||
"txid": "78b64023700c50e221d68c093cc3fe32a5314e9696bacb1b6d10217d51664d68",
|
||||
"hash": "f072bb4fec0ae128ed678d24475cf719c0dd3ba0b44bf14f7300df60408ee365",
|
||||
"version": 1,
|
||||
"size": 255,
|
||||
"vsize": 173,
|
||||
"weight": 690,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "376a560724612cfc3249e8f93f1a4156fc1b62678716290cbd326e16b509f6c3",
|
||||
"vout": 2,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"txinwitness": [
|
||||
"30450221008f8eee212f209ba2a179197bd4ba35a35ad7a304599025ddbd192a6e7e64c1920220242c297726948ad408ce54c9a0e0287b283c53dc68323537f24a7e3ecd8c526b01",
|
||||
"03f870bcd3a46e80e4b1236302e62318b412cc97ef096fc976a89deb569dc11ef1"
|
||||
],
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 5.0E-5,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_HASH160 4c0e4893237f85479f489b32c8ff0faf3ee2e1c9 OP_EQUAL",
|
||||
"hex": "a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987",
|
||||
"reqSigs": 1,
|
||||
"type": "scripthash",
|
||||
"addresses": [
|
||||
"2MzBNTJDjjXgViKBGnatDU3yWkJ8pJkEg9w"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.016,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 1437b91493d1929b1b42a80e83229c347c28f937",
|
||||
"hex": "00141437b91493d1929b1b42a80e83229c347c28f937",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1qzsmmj9yn6xffkx6z4q8gxg5ux37z37fhr598r9"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 1.10145514,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 91ad2cce99e8e4455de5f44559816b98213f3503",
|
||||
"hex": "001491ad2cce99e8e4455de5f44559816b98213f3503",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1qjxkjen5earjy2h0973z4nqttnqsn7dgrnsgy27"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"hex": "01000000000101c3f609b5166e32bd0c29168767621bfc56411a3ff9e84932fc2c612407566a370200000000ffffffff03881300000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987006a1800000000001600141437b91493d1929b1b42a80e83229c347c28f937eaaf90060000000016001491ad2cce99e8e4455de5f44559816b98213f3503024830450221008f8eee212f209ba2a179197bd4ba35a35ad7a304599025ddbd192a6e7e64c1920220242c297726948ad408ce54c9a0e0287b283c53dc68323537f24a7e3ecd8c526b012103f870bcd3a46e80e4b1236302e62318b412cc97ef096fc976a89deb569dc11ef100000000"
|
||||
},
|
||||
{
|
||||
"txid": "dd4243c2743a2d2351814a14628e1976e6fb208e63bd2bbd441180c441205027",
|
||||
"hash": "dd4243c2743a2d2351814a14628e1976e6fb208e63bd2bbd441180c441205027",
|
||||
"version": 1,
|
||||
"size": 252,
|
||||
"vsize": 252,
|
||||
"weight": 1008,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "20bd92c145d9d724283c9536355a82e6fd87637963dd7e0c1fb87c9de50a4fba",
|
||||
"vout": 2,
|
||||
"scriptSig": {
|
||||
"asm": "3045022100ba97f6336b3bb3e07cf584010c7b8ab52957e34e462e71252c63f498d51f45b70220708dd78d2d9943f8c176055963ab706870274068fe7b1e5d87592a837336a534[ALL] 039978f14b2463d7d4790cdf2a37c2a3d872dd517ca91db7f6f7a858a7ac661c60",
|
||||
"hex": "483045022100ba97f6336b3bb3e07cf584010c7b8ab52957e34e462e71252c63f498d51f45b70220708dd78d2d9943f8c176055963ab706870274068fe7b1e5d87592a837336a5340121039978f14b2463d7d4790cdf2a37c2a3d872dd517ca91db7f6f7a858a7ac661c60"
|
||||
},
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 5.0E-5,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_HASH160 4c0e4893237f85479f489b32c8ff0faf3ee2e1c9 OP_EQUAL",
|
||||
"hex": "a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987",
|
||||
"reqSigs": 1,
|
||||
"type": "scripthash",
|
||||
"addresses": [
|
||||
"2MzBNTJDjjXgViKBGnatDU3yWkJ8pJkEg9w"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.006,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 13afc3a26c010dddaf2410a9d97b5054a4e8d309",
|
||||
"hex": "001413afc3a26c010dddaf2410a9d97b5054a4e8d309",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1qzwhu8gnvqyxamteyzz5aj76s2jjw35cfctjvy8"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 9.45414148,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 5228ee46a95383b314396dda75e707b8bed83034",
|
||||
"hex": "00145228ee46a95383b314396dda75e707b8bed83034",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1q2g5wu34f2wpmx9pedhd8tec8hzldsvp56hljn8"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"hex": "0100000001ba4f0ae59d7cb81f0c7edd63796387fde6825a3536953c2824d7d945c192bd20020000006b483045022100ba97f6336b3bb3e07cf584010c7b8ab52957e34e462e71252c63f498d51f45b70220708dd78d2d9943f8c176055963ab706870274068fe7b1e5d87592a837336a5340121039978f14b2463d7d4790cdf2a37c2a3d872dd517ca91db7f6f7a858a7ac661c60ffffffff03881300000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987c02709000000000016001413afc3a26c010dddaf2410a9d97b5054a4e8d30904e05938000000001600145228ee46a95383b314396dda75e707b8bed8303400000000"
|
||||
},
|
||||
{
|
||||
"txid": "aa33deb87512c2c417a0a9a17c58a36dcf2686fca8b50cc608ad952178a350c2",
|
||||
"hash": "aa33deb87512c2c417a0a9a17c58a36dcf2686fca8b50cc608ad952178a350c2",
|
||||
"version": 1,
|
||||
"size": 251,
|
||||
"vsize": 251,
|
||||
"weight": 1004,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "7ac20c807aac72bc1cc57ef25f61567f74a031952b0a18618bc06be504dc715d",
|
||||
"vout": 2,
|
||||
"scriptSig": {
|
||||
"asm": "3044022073a8a8ee9cc490093e6de5708b3727cef35f41038713fab9a5c235b4b400b73102206f97f4fc8faefb534e85c0f3773f2d67781c9871f681211276619054fc540152[ALL] 0374e07f24beca2270cf305100652149a64c80d76611f775ec276658aeae4ef0b5",
|
||||
"hex": "473044022073a8a8ee9cc490093e6de5708b3727cef35f41038713fab9a5c235b4b400b73102206f97f4fc8faefb534e85c0f3773f2d67781c9871f681211276619054fc54015201210374e07f24beca2270cf305100652149a64c80d76611f775ec276658aeae4ef0b5"
|
||||
},
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 5.0E-5,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_HASH160 4c0e4893237f85479f489b32c8ff0faf3ee2e1c9 OP_EQUAL",
|
||||
"hex": "a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987",
|
||||
"reqSigs": 1,
|
||||
"type": "scripthash",
|
||||
"addresses": [
|
||||
"2MzBNTJDjjXgViKBGnatDU3yWkJ8pJkEg9w"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.016,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 1c397ba7ea8410dbd556c51f8184a14ab0151143",
|
||||
"hex": "00141c397ba7ea8410dbd556c51f8184a14ab0151143",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1qrsuhhfl2ssgdh42kc50crp9pf2cp2y2ruplr8l"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 1.1126122,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 c38c3d890c415f2c13f6925c1ad1d4a7cb4f7dfe",
|
||||
"hex": "0014c38c3d890c415f2c13f6925c1ad1d4a7cb4f7dfe",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1qcwxrmzgvg90jcylkjfwp45w55l957l07mtsydu"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"hex": "01000000015d71dc04e56bc08b61180a2b9531a0747f56615ff27ec51cbc72ac7a800cc27a020000006a473044022073a8a8ee9cc490093e6de5708b3727cef35f41038713fab9a5c235b4b400b73102206f97f4fc8faefb534e85c0f3773f2d67781c9871f681211276619054fc54015201210374e07f24beca2270cf305100652149a64c80d76611f775ec276658aeae4ef0b5ffffffff03881300000000000017a9144c0e4893237f85479f489b32c8ff0faf3ee2e1c987006a1800000000001600141c397ba7ea8410dbd556c51f8184a14ab015114324b6a10600000000160014c38c3d890c415f2c13f6925c1ad1d4a7cb4f7dfe00000000"
|
||||
},
|
||||
{
|
||||
"txid": "719606705c6832f7180ab9db5e1ce6a51bad80a5cbf3b57b806dd10f3d7d5124",
|
||||
"hash": "c670f473daee2ad2fabc2b8cc9e99d499c19ef9f2d3331345674a0d3b1b96639",
|
||||
"version": 1,
|
||||
"size": 425,
|
||||
"vsize": 263,
|
||||
"weight": 1052,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "d21edc2a9ffd0f90982fcdd0ee11a48bc3f78bf437232354f7ad60740a202a14",
|
||||
"vout": 1,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"txinwitness": [
|
||||
"30440220073a37eb4371dc3d0cf218d6e9b8e6044275acd07402ccebdf24f65b60a3c1f70220647e71c173f992fc0c5ec6c2b0b1653a95118098269e318c41d1bc33da3ff14f01",
|
||||
"03c3e858472f39d31c6defdf38b4778660501f0ccfa524b3dd8ba61117b7646635"
|
||||
],
|
||||
"sequence": 4294967295
|
||||
},
|
||||
{
|
||||
"txid": "f52b22f8c5156fb51122a90b8dc39989c6912c02a69e22bd3148b0692a7cd699",
|
||||
"vout": 1,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"txinwitness": [
|
||||
"304402202b8ef5de1c56328d3797265272540a054fc04c158b23ee6385b69b14486422c10220749f591fcf4ef995df8f1e7d9aa3cf0c045f1616386b86b419197b360c871fca01",
|
||||
"02c73e60f00bc72b56568a9f371b9122b3ee29d41730670e98ff8da58e7bbfab28"
|
||||
],
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 1.0E-5,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 223d978073802f79e6ecdc7591e5dc1f0ea7030d6466f73c6b90391bc72e886f",
|
||||
"hex": "0020223d978073802f79e6ecdc7591e5dc1f0ea7030d6466f73c6b90391bc72e886f",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_scripthash",
|
||||
"addresses": [
|
||||
"bcrt1qyg7e0qrnsqhhnehvm36erewuru82wqcdv3n0w0rtjqu3h3ew3phs27rfy6"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.0,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_RETURN 758e9207848c631c6839b1382bb22a52b6ef0645d733389d7be2efb1e8b71454",
|
||||
"hex": "6a20758e9207848c631c6839b1382bb22a52b6ef0645d733389d7be2efb1e8b71454",
|
||||
"type": "nulldata"
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.02201563,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 5e41d2fb8de1c250410416d8dd153c685d3f9c7b",
|
||||
"hex": "00145e41d2fb8de1c250410416d8dd153c685d3f9c7b",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1qteqa97udu8p9qsgyzmvd69fudpwnl8rmt8putj"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"hex": "01000000000102142a200a7460adf754232337f48bf7c38ba411eed0cd2f98900ffd9f2adc1ed20100000000ffffffff99d67c2a69b04831bd229ea6022c91c68999c38d0ba92211b56f15c5f8222bf50100000000ffffffff03e803000000000000220020223d978073802f79e6ecdc7591e5dc1f0ea7030d6466f73c6b90391bc72e886f0000000000000000226a20758e9207848c631c6839b1382bb22a52b6ef0645d733389d7be2efb1e8b71454db972100000000001600145e41d2fb8de1c250410416d8dd153c685d3f9c7b024730440220073a37eb4371dc3d0cf218d6e9b8e6044275acd07402ccebdf24f65b60a3c1f70220647e71c173f992fc0c5ec6c2b0b1653a95118098269e318c41d1bc33da3ff14f012103c3e858472f39d31c6defdf38b4778660501f0ccfa524b3dd8ba61117b76466350247304402202b8ef5de1c56328d3797265272540a054fc04c158b23ee6385b69b14486422c10220749f591fcf4ef995df8f1e7d9aa3cf0c045f1616386b86b419197b360c871fca012102c73e60f00bc72b56568a9f371b9122b3ee29d41730670e98ff8da58e7bbfab2800000000"
|
||||
},
|
||||
{
|
||||
"txid": "16cd3283068d2965f26045e15a285e39a762af32ec388ae8c28fb6cb2c468768",
|
||||
"hash": "a854736ce90b603599bf21be28bea909c3ffafaed57d5eacea1f1730b68db0d8",
|
||||
"version": 1,
|
||||
"size": 427,
|
||||
"vsize": 264,
|
||||
"weight": 1054,
|
||||
"locktime": 0,
|
||||
"vin": [
|
||||
{
|
||||
"txid": "4648da84e1479d222561f72c5211be15510b20129e86c60bdbbdb3e017583a91",
|
||||
"vout": 1,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"txinwitness": [
|
||||
"3045022100c9665b9abe7fcab10f775eeafc2391c1fee84c50b50df6d697b1db9a7eea5dd3022070cb7aa57b8f5bf9f2eff11263cf2ea871ab7b9ddfc8e47671cee50ada54724301",
|
||||
"02016f9a6cc454bd1e74c28df36a079231a215812c60581d1e1745e650f82bd123"
|
||||
],
|
||||
"sequence": 4294967295
|
||||
},
|
||||
{
|
||||
"txid": "78b64023700c50e221d68c093cc3fe32a5314e9696bacb1b6d10217d51664d68",
|
||||
"vout": 1,
|
||||
"scriptSig": {
|
||||
"asm": "",
|
||||
"hex": ""
|
||||
},
|
||||
"txinwitness": [
|
||||
"304502210094cdec8e08f32919b3f25c8672041305c848b4206256ad64f7090dc97dfd1bf002205c397a310cebc690fac04d13945e012d0031246d4574e92f97e3701ca729b61401",
|
||||
"029b739486d7cf402b3db3913187fad7897e8a5ec3cd8607e9b5fc54a71958b031"
|
||||
],
|
||||
"sequence": 4294967295
|
||||
}
|
||||
],
|
||||
"vout": [
|
||||
{
|
||||
"value": 1.0E-5,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 e3e81046fd9659b5725736efa404bc1c8f9b2ff6f0af7cb7ddacdcc61e1c7231",
|
||||
"hex": "0020e3e81046fd9659b5725736efa404bc1c8f9b2ff6f0af7cb7ddacdcc61e1c7231",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_scripthash",
|
||||
"addresses": [
|
||||
"bcrt1qu05pq3hajevm2ujhxmh6gp9urj8ektlk7zhhed7a4nwvv8suwgcstjh7k7"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.0,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "OP_RETURN c63aacf2e8be20752b6f689c0308967cbc335641f2948a4a7962fdde6c464730",
|
||||
"hex": "6a20c63aacf2e8be20752b6f689c0308967cbc335641f2948a4a7962fdde6c464730",
|
||||
"type": "nulldata"
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 0.0220133,
|
||||
"n": 2,
|
||||
"scriptPubKey": {
|
||||
"asm": "0 5e41d2fb8de1c250410416d8dd153c685d3f9c7b",
|
||||
"hex": "00145e41d2fb8de1c250410416d8dd153c685d3f9c7b",
|
||||
"reqSigs": 1,
|
||||
"type": "witness_v0_keyhash",
|
||||
"addresses": [
|
||||
"bcrt1qteqa97udu8p9qsgyzmvd69fudpwnl8rmt8putj"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"hex": "01000000000102913a5817e0b3bddb0bc6869e12200b5115be11522cf76125229d47e184da48460100000000ffffffff684d66517d21106d1bcbba96964e31a532fec33c098cd621e2500c702340b6780100000000ffffffff03e803000000000000220020e3e81046fd9659b5725736efa404bc1c8f9b2ff6f0af7cb7ddacdcc61e1c72310000000000000000226a20c63aacf2e8be20752b6f689c0308967cbc335641f2948a4a7962fdde6c464730f2962100000000001600145e41d2fb8de1c250410416d8dd153c685d3f9c7b02483045022100c9665b9abe7fcab10f775eeafc2391c1fee84c50b50df6d697b1db9a7eea5dd3022070cb7aa57b8f5bf9f2eff11263cf2ea871ab7b9ddfc8e47671cee50ada547243012102016f9a6cc454bd1e74c28df36a079231a215812c60581d1e1745e650f82bd1230248304502210094cdec8e08f32919b3f25c8672041305c848b4206256ad64f7090dc97dfd1bf002205c397a310cebc690fac04d13945e012d0031246d4574e92f97e3701ca729b6140121029b739486d7cf402b3db3913187fad7897e8a5ec3cd8607e9b5fc54a71958b03100000000"
|
||||
}
|
||||
],
|
||||
"time": 1605510591,
|
||||
"mediantime": 1589548514,
|
||||
"nonce": 1,
|
||||
"bits": "207fffff",
|
||||
"difficulty": 4.656542373906925E-10,
|
||||
"chainwork": "0000000000000000000000000000000000000000000000000000000000000118",
|
||||
"nTx": 11,
|
||||
"previousblockhash": "1a477356be8211dd4b3a4162521331a6af9b5999401aad8d147a56a1f28c3da3",
|
||||
"nextblockhash": "7d8267341a57f1f626b450eb22b2dbf208f13ec176e1cc015aa4d2b2ea55016d"
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"version": 210000,
|
||||
"subversion": "/Satoshi:0.21.0/",
|
||||
"protocolversion": 70016,
|
||||
"localservices": "000000000100040d",
|
||||
"localservicesnames": [
|
||||
"NETWORK",
|
||||
"BLOOM",
|
||||
"WITNESS",
|
||||
"NETWORK_LIMITED",
|
||||
"MY_CUSTOM_SERVICE"
|
||||
],
|
||||
"localrelay": true,
|
||||
"timeoffset": -2,
|
||||
"networkactive": true,
|
||||
"connections": 9,
|
||||
"connections_in": 0,
|
||||
"connections_out": 9,
|
||||
"networks": [
|
||||
{
|
||||
"name": "ipv4",
|
||||
"limited": false,
|
||||
"reachable": true,
|
||||
"proxy": "",
|
||||
"proxy_randomize_credentials": false
|
||||
},
|
||||
{
|
||||
"name": "ipv6",
|
||||
"limited": false,
|
||||
"reachable": true,
|
||||
"proxy": "",
|
||||
"proxy_randomize_credentials": false
|
||||
},
|
||||
{
|
||||
"name": "onion",
|
||||
"limited": true,
|
||||
"reachable": false,
|
||||
"proxy": "",
|
||||
"proxy_randomize_credentials": false
|
||||
}
|
||||
],
|
||||
"relayfee": 1.0E-5,
|
||||
"incrementalfee": 1.0E-5,
|
||||
"localaddresses": [
|
||||
{
|
||||
"address": "2001:0:2851:782c:2c65:3676:fde6:18f8",
|
||||
"port": 8333,
|
||||
"score": 26
|
||||
}
|
||||
],
|
||||
"warnings": ""
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"37fba8bf119c289481eef031c0a35e126376f71d13d7cce35eb0d5e05799b5da": "hUWPf,37fba8bf119c289481eef031c0a35e126376f71d13d7cce35eb0d5e05799b5da,19910000,200,0,668994, tx_missing_from_blockchain_for_4_days",
|
||||
"b3bc726aa2aa6533cb1e61901ce351eecde234378fe650aee267388886aa6e4b": "ebdttmzh,b3bc726aa2aa6533cb1e61901ce351eecde234378fe650aee267388886aa6e4b,4000000,5000,1,669137, tx_missing_from_blockchain_for_2_days",
|
||||
"10f32fe53081466f003185a9ef0324d6cbe3f59334ee9ccb2f7155cbfad9c1de": "kmbyoexc,10f32fe53081466f003185a9ef0324d6cbe3f59334ee9ccb2f7155cbfad9c1de,33000000,332,0,668954, tx_not_found",
|
||||
"cd99836ac4246c3e3980edf95773060481ce52271b74dadeb41e18c42ed21188": "nlaIlAvE,cd99836ac4246c3e3980edf95773060481ce52271b74dadeb41e18c42ed21188,5000000,546,1,669262, invalid_missing_fee_address",
|
||||
"fc3cb16293895fea8ea5d2d8ab4e39d1b27f583e2c160468b586789a861efa74": "feescammer,fc3cb16293895fea8ea5d2d8ab4e39d1b27f583e2c160468b586789a861efa74,1000000,546,1,669442, invalid_missing_fee_address",
|
||||
"72cabb5c323c923b43c7f6551974f591dcee148778ee34f9131011ea0ca82813": "PBFICEAS,72cabb5c323c923b43c7f6551974f591dcee148778ee34f9131011ea0ca82813,2000000,546,1,672969, dust_fee_scammer",
|
||||
"1c8e4934f93b5bbd2823318d5d491698316216f2e4bc0d7cd353f6b16358d80e": "feescammer,1c8e4934f93b5bbd2823318d5d491698316216f2e4bc0d7cd353f6b16358d80e,2000000,546,1,669227, dust_fee_scammer",
|
||||
"17cbd95d8809dc8808a5c209208f59c4a80e09e012e62951668d30d716c44a96": "feescammer,17cbd95d8809dc8808a5c209208f59c4a80e09e012e62951668d30d716c44a96,2000000,546,1,669340, dust_fee_scammer"
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"4cdea8872a7d96210f378e0221dc1aae8ee9abb282582afa7546890fb39b7189": "am7DzIv,4cdea8872a7d96210f378e0221dc1aae8ee9abb282582afa7546890fb39b7189,6100000,35,0,668195, underpaid but accepted due to use of different DAO parameter",
|
||||
"ef1ea38b46402deb7df08c13a6dc379a65542a6940ac9d4ba436641ffd4bcb6e": "FQ0A7G,ef1ea38b46402deb7df08c13a6dc379a65542a6940ac9d4ba436641ffd4bcb6e,15970000,92,0,640438, underpaid but accepted due to use of different DAO parameter",
|
||||
"051770f8d7f43a9b6ca10fefa6cdf4cb124a81eed26dc8af2e40f57d2589107b": "046698,051770f8d7f43a9b6ca10fefa6cdf4cb124a81eed26dc8af2e40f57d2589107b,15970000,92,0,667927, bsq fee underpaid using 5.75 rate for some weird reason",
|
||||
"e125fdbd09ee86c01e16e1f12a31507cfb8703ed1bd5a221461adf33cb3e00d9": "7213472,e125fdbd09ee86c01e16e1f12a31507cfb8703ed1bd5a221461adf33cb3e00d9,200000000,200000,1,578672, unknown_fee_receiver_1PUXU1MQ",
|
||||
"44b00de808d0145f9a948fe1b020c5d4173402ba0b5a5ba69124c67e371bca18": "aAPLmh98,44b00de808d0145f9a948fe1b020c5d4173402ba0b5a5ba69124c67e371bca18,140000000,140000,1,578629, unknown_fee_receiver_1PUXU1MQ",
|
||||
"654a7a34321b57be6a553052d1d9e0f1764dd2fab7b64c9422e9953e4d9d127d": "pwdbdku,654a7a34321b57be6a553052d1d9e0f1764dd2fab7b64c9422e9953e4d9d127d,24980000,238000,1,554947, unknown_fee_receiver_18GzH11",
|
||||
"0636bafb14890edfb95465e66e2b1e15915f7fb595f9b653b9129c15ef4c1c4b": "msimscqb,0636bafb14890edfb95465e66e2b1e15915f7fb595f9b653b9129c15ef4c1c4b,1000000,10,0,662390",
|
||||
"2861f4526f40686d5cddc364035b561c13625996233a8b8705195041504ba3a1": "89284,2861f4526f40686d5cddc364035b561c13625996233a8b8705195041504ba3a1,900000,9,0,666473",
|
||||
"a571e8a2e9227025f897017db0a7cbd7baea98d7f119aea49c46d6535d79caba": "EHGVHSL,a571e8a2e9227025f897017db0a7cbd7baea98d7f119aea49c46d6535d79caba,1000000,5000,1,665825",
|
||||
"ac001c7eff1cfaaf45f955a9a353f113153cd21610e5e8449b15559592b25d6e": "M2CNGNN,ac001c7eff1cfaaf45f955a9a353f113153cd21610e5e8449b15559592b25d6e,600000,6,0,669043",
|
||||
"cdd49f58806253abfa6f6566d0659c2f51c28256ef19acdde6a23331d6f07348": "qHBsg,cdd49f58806253abfa6f6566d0659c2f51c28256ef19acdde6a23331d6f07348,25840000,258,0,611324",
|
||||
"aaf29059ba14264d9fa85fe6700c13b36b3b4aa2748745eafefabcf276dc2c25": "87822,aaf29059ba14264d9fa85fe6700c13b36b3b4aa2748745eafefabcf276dc2c25,1000000,10,0,668839",
|
||||
"9ab825e4eb298ceb74237faf576c0f5088430fdf05e61a4e9ae4028508b318e6": "9134295,9ab825e4eb298ceb74237faf576c0f5088430fdf05e61a4e9ae4028508b318e6,30000000,30000,1,666606",
|
||||
"768df499434d48f6dc3329e0abfd3bbc930b884b2caff9b7e4d7f1ec15c4c28d": "5D4EQC,768df499434d48f6dc3329e0abfd3bbc930b884b2caff9b7e4d7f1ec15c4c28d,10000000,101,0,668001",
|
||||
"9b517de9ef9c00a779b58271a301347e13fc9525be42100452f462526a6f8523": "23608,9b517de9ef9c00a779b58271a301347e13fc9525be42100452f462526a6f8523,5000000,5000,1,668593",
|
||||
"02f8976ca80f98f095c5656675aa6f40aafced65451917443c1e5057186f2592": "I3WzjuF,02f8976ca80f98f095c5656675aa6f40aafced65451917443c1e5057186f2592,1000000,10,0,666563",
|
||||
"995de91e69e2590aff67ae6e4f2d417bad6882b11cc095b2420fef7506209be8": "WlvThoI,995de91e69e2590aff67ae6e4f2d417bad6882b11cc095b2420fef7506209be8,1000000,5000,1,669231",
|
||||
"ca4a1f991c3f585e4fbbb5b5aeb0766ba3eb46bb1c3ff3714db7a8cadd0e557d": "ffhpgz0z,ca4a1f991c3f585e4fbbb5b5aeb0766ba3eb46bb1c3ff3714db7a8cadd0e557d,2000000,20,0,667351",
|
||||
"b9e1a791f4091910caeb70d1a5d56452bc9614c16b5b74281b2485551faeb46e": "jgtwzsn,b9e1a791f4091910caeb70d1a5d56452bc9614c16b5b74281b2485551faeb46e,1000000,10,0,666372",
|
||||
"dc06cd41f4b778553a0a5df4578c62eeb0c9c878b6f1a24c60b619a6749877c7": "AZhkSO,dc06cd41f4b778553a0a5df4578c62eeb0c9c878b6f1a24c60b619a6749877c7,200000000,200000,1,668526"
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"44b00de808d0145f9a948fe1b020c5d4173402ba0b5a5ba69124c67e371bca18": "{\"txid\":\"44b00de808d0145f9a948fe1b020c5d4173402ba0b5a5ba69124c67e371bca18\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":1,\"prevout\":{\"value\":147186800}}],\"vout\":[{\"scriptpubkey_address\":\"1PUXU1MQ82JC3Hx1NN5tZs3BaTAJVg72MC\",\"value\":140000},{\"scriptpubkey_address\":\"1HwN7DhxNQdFKzMbrQq5vRHzY4xXGTRcne\",\"value\":147000000}],\"size\":226,\"weight\":904,\"fee\":46800,\"status\":{\"confirmed\":true,\"block_height\":578630}}",
|
||||
"2861f4526f40686d5cddc364035b561c13625996233a8b8705195041504ba3a1": "{\"txid\":\"2861f4526f40686d5cddc364035b561c13625996233a8b8705195041504ba3a1\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":1393}},{\"vout\":1,\"prevout\":{\"value\":600000}},{\"vout\":2,\"prevout\":{\"value\":10872788}}],\"vout\":[{\"scriptpubkey_address\":\"1NsTgbTUKhveanGCmsawJKLf6asQhJP4p2\",\"value\":1384},{\"scriptpubkey_address\":\"bc1qlw44hxyqfwcmcuuvtktduhth5ah4djl63sc4eq\",\"value\":1500000},{\"scriptpubkey_address\":\"bc1qyty4urzh25j5qypqu7v9mzhwt3p0zvaxeehpxp\",\"value\":9967337}],\"size\":552,\"weight\":1557,\"fee\":5460,\"status\":{\"confirmed\":true,\"block_height\":666479}}",
|
||||
"0636bafb14890edfb95465e66e2b1e15915f7fb595f9b653b9129c15ef4c1c4b": "{\"txid\":\"0636bafb14890edfb95465e66e2b1e15915f7fb595f9b653b9129c15ef4c1c4b\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":7899}},{\"vout\":2,\"prevout\":{\"value\":54877439}}],\"vout\":[{\"scriptpubkey_address\":\"1FCUu7hqKCSsGhVJaLbGEoCWdZRJRNqq8w\",\"value\":7889},{\"scriptpubkey_address\":\"bc1qkj5l4wxl00ufdx6ygcnrck9fz5u927gkwqcgey\",\"value\":1600000},{\"scriptpubkey_address\":\"bc1qkw4a8u9l5w9fhdh3ue9v7e7celk4jyudzg5gk5\",\"value\":53276799}],\"size\":405,\"weight\":1287,\"fee\":650,\"status\":{\"confirmed\":true,\"block_height\":663140}}",
|
||||
"a571e8a2e9227025f897017db0a7cbd7baea98d7f119aea49c46d6535d79caba": "{\"txid\":\"a571e8a2e9227025f897017db0a7cbd7baea98d7f119aea49c46d6535d79caba\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":2,\"prevout\":{\"value\":798055}}],\"vout\":[{\"scriptpubkey_address\":\"38bZBj5peYS3Husdz7AH3gEUiUbYRD951t\",\"value\":5000},{\"scriptpubkey_address\":\"bc1qy69ekanm2twzqqr7vz9qcxypyta29wdm2t0ay8\",\"value\":600000},{\"scriptpubkey_address\":\"bc1qp6q2urrntp8tq67lhymftsq0dpqvqmpnus7hym\",\"value\":184830}],\"size\":254,\"weight\":689,\"fee\":8225,\"status\":{\"confirmed\":true,\"block_height\":665826}}",
|
||||
"ac001c7eff1cfaaf45f955a9a353f113153cd21610e5e8449b15559592b25d6e": "{\"txid\":\"ac001c7eff1cfaaf45f955a9a353f113153cd21610e5e8449b15559592b25d6e\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":4165}},{\"vout\":2,\"prevout\":{\"value\":593157}},{\"vout\":2,\"prevout\":{\"value\":595850}}],\"vout\":[{\"scriptpubkey_address\":\"16Y1WqYEbWygHz6kuhJxWXos3bw46JNNoZ\",\"value\":4159},{\"scriptpubkey_address\":\"bc1qkxjvjp2hyegjpw5jtlju7fcr7pv9en3u7cg7q7\",\"value\":600000},{\"scriptpubkey_address\":\"bc1q9x95y8ktsxg9jucky66da3v2s2har56cy3nkkg\",\"value\":575363}],\"size\":555,\"weight\":1563,\"fee\":13650,\"status\":{\"confirmed\":true,\"block_height\":669045}}",
|
||||
"cdd49f58806253abfa6f6566d0659c2f51c28256ef19acdde6a23331d6f07348": "{\"txid\":\"cdd49f58806253abfa6f6566d0659c2f51c28256ef19acdde6a23331d6f07348\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":4898}},{\"vout\":2,\"prevout\":{\"value\":15977562}}],\"vout\":[{\"scriptpubkey_address\":\"16SCUfnCLddxgoAYLUcsJcoE4VxBRzgTSz\",\"value\":4640},{\"scriptpubkey_address\":\"1N9Pb6DTJXh96QjzYLDFTZuBvFXgFPi18N\",\"value\":1292000},{\"scriptpubkey_address\":\"1C7tg4KT9wQvLR5xfPDqD4U35Ncwk3UQxm\",\"value\":14681720}],\"size\":406,\"weight\":1624,\"fee\":4100,\"status\":{\"confirmed\":true,\"block_height\":611325}}",
|
||||
"aaf29059ba14264d9fa85fe6700c13b36b3b4aa2748745eafefabcf276dc2c25": "{\"txid\":\"aaf29059ba14264d9fa85fe6700c13b36b3b4aa2748745eafefabcf276dc2c25\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":3512}},{\"vout\":2,\"prevout\":{\"value\":1481349}},{\"vout\":1,\"prevout\":{\"value\":600000}}],\"vout\":[{\"scriptpubkey_address\":\"14rNP2aC23hr6u8ALmksm3RgJys7CAD3No\",\"value\":3502},{\"scriptpubkey_address\":\"bc1qvctcjcrhznptmydv4hxwc4wd2km76shkl3jj29\",\"value\":1600000},{\"scriptpubkey_address\":\"bc1qsdzpvr6sehypswcwjsmmjzctjhy5hkwqvf2vh8\",\"value\":476289}],\"size\":555,\"weight\":1563,\"fee\":5070,\"status\":{\"confirmed\":true,\"block_height\":668841}}",
|
||||
"9ab825e4eb298ceb74237faf576c0f5088430fdf05e61a4e9ae4028508b318e6": "{\"txid\":\"9ab825e4eb298ceb74237faf576c0f5088430fdf05e61a4e9ae4028508b318e6\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":4533500}}],\"vout\":[{\"scriptpubkey_address\":\"1EKXx73oUhHaUh8JBimtiPGgHfwNmxYKAj\",\"value\":30000},{\"scriptpubkey_address\":\"bc1qde7asrvrnkn5st5q8u038fxt9tlrgyaxwju6hn\",\"value\":4500000}],\"size\":226,\"weight\":574,\"fee\":3500,\"status\":{\"confirmed\":true,\"block_height\":666607}}",
|
||||
"768df499434d48f6dc3329e0abfd3bbc930b884b2caff9b7e4d7f1ec15c4c28d": "{\"txid\":\"768df499434d48f6dc3329e0abfd3bbc930b884b2caff9b7e4d7f1ec15c4c28d\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":249699}},{\"vout\":1,\"prevout\":{\"value\":4023781}}],\"vout\":[{\"scriptpubkey_address\":\"1J8wtmJuurfBSrRb27urtoHzuQey1ipXPX\",\"value\":249598},{\"scriptpubkey_address\":\"bc1qfmyw7pwaqucprcsauqr6gvez9wep290r4amd3y\",\"value\":1500000},{\"scriptpubkey_address\":\"bc1q2lx0fymd3mmk4pzjq2k8hn7mk3hnctnjtu497t\",\"value\":2517382}],\"size\":405,\"weight\":1287,\"fee\":6500,\"status\":{\"confirmed\":true,\"block_height\":668002}}",
|
||||
"02f8976ca80f98f095c5656675aa6f40aafced65451917443c1e5057186f2592": "{\"txid\":\"02f8976ca80f98f095c5656675aa6f40aafced65451917443c1e5057186f2592\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":33860}},{\"vout\":1,\"prevout\":{\"value\":600000}},{\"vout\":2,\"prevout\":{\"value\":39304}},{\"vout\":3,\"prevout\":{\"value\":5000000}}],\"vout\":[{\"scriptpubkey_address\":\"1Le1auzXSpEnyMc6S9KNentnye3gTPLnuA\",\"value\":33850},{\"scriptpubkey_address\":\"bc1qs73jfmjzclsx9466pvpslfuqc2kkv5uc8u928a\",\"value\":1600000},{\"scriptpubkey_address\":\"bc1q85zlv50mddyuerze7heve0vcv4f80qsw2szv34\",\"value\":4031088}],\"size\":701,\"weight\":1829,\"fee\":8226,\"status\":{\"confirmed\":true,\"block_height\":666564}}",
|
||||
"9b517de9ef9c00a779b58271a301347e13fc9525be42100452f462526a6f8523": "{\"txid\":\"9b517de9ef9c00a779b58271a301347e13fc9525be42100452f462526a6f8523\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":2,\"prevout\":{\"value\":4466600}}],\"vout\":[{\"scriptpubkey_address\":\"38bZBj5peYS3Husdz7AH3gEUiUbYRD951t\",\"value\":5000},{\"scriptpubkey_address\":\"bc1qrl0dvwp6hpqlcj65qfhl70lz67yjvhlc8z73a4\",\"value\":750000},{\"scriptpubkey_address\":\"bc1qg55gnkhgg4zltdh76sdef33xzr7h95g3xsxesg\",\"value\":3709675}],\"size\":254,\"weight\":689,\"fee\":1925,\"status\":{\"confirmed\":true,\"block_height\":668843}}",
|
||||
"995de91e69e2590aff67ae6e4f2d417bad6882b11cc095b2420fef7506209be8": "{\"txid\":\"995de91e69e2590aff67ae6e4f2d417bad6882b11cc095b2420fef7506209be8\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":2,\"prevout\":{\"value\":36006164}}],\"vout\":[{\"scriptpubkey_address\":\"19qA2BVPoyXDfHKVMovKG7SoxGY7xrBV8c\",\"value\":5000},{\"scriptpubkey_address\":\"bc1qefxyxsq9tskaw0qarxf0hdxusxe64l8zsmsgrz\",\"value\":1600000},{\"scriptpubkey_address\":\"bc1q6fky0fxcg3zrz5t0xdyq5sh90h7m5sya0wf9gx\",\"value\":34390664}],\"size\":256,\"weight\":697,\"fee\":10500,\"status\":{\"confirmed\":true,\"block_height\":669233}}",
|
||||
"fc3cb16293895fea8ea5d2d8ab4e39d1b27f583e2c160468b586789a861efa74": "{\"txid\":\"fc3cb16293895fea8ea5d2d8ab4e39d1b27f583e2c160468b586789a861efa74\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":1,\"prevout\":{\"value\":49144}},{\"vout\":0,\"prevout\":{\"value\":750000}}],\"vout\":[{\"scriptpubkey_address\":\"bc1qgsx9y62ajme3gg8v9n9jfps2694uy9r6f9unj0\",\"value\":600000},{\"scriptpubkey_address\":\"bc1q6lqf0jehmaadwmdhap98rulflft27z00g0qphn\",\"value\":187144}],\"size\":372,\"weight\":834,\"fee\":12000,\"status\":{\"confirmed\":true,\"block_height\":669442}}",
|
||||
"ca4a1f991c3f585e4fbbb5b5aeb0766ba3eb46bb1c3ff3714db7a8cadd0e557d": "{\"txid\":\"ca4a1f991c3f585e4fbbb5b5aeb0766ba3eb46bb1c3ff3714db7a8cadd0e557d\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":10237}},{\"vout\":1,\"prevout\":{\"value\":600000}},{\"vout\":1,\"prevout\":{\"value\":900000}},{\"vout\":2,\"prevout\":{\"value\":8858290}}],\"vout\":[{\"scriptpubkey_address\":\"1DQXP1AXR1qkXficdkfXHHy2JkbtRGFQ1b\",\"value\":10217},{\"scriptpubkey_address\":\"bc1q9jfjvhvr42smvwylrlqcrefcdxagdzf52aquzm\",\"value\":2600000},{\"scriptpubkey_address\":\"bc1qc6qraj5h8qxvluh2um4rvunqn68fltc9kjfrk9\",\"value\":7753730}],\"size\":702,\"weight\":1833,\"fee\":4580,\"status\":{\"confirmed\":true,\"block_height\":667352}}",
|
||||
"4cdea8872a7d96210f378e0221dc1aae8ee9abb282582afa7546890fb39b7189": "{\"txid\":\"4cdea8872a7d96210f378e0221dc1aae8ee9abb282582afa7546890fb39b7189\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":23893}},{\"vout\":1,\"prevout\":{\"value\":1440000}},{\"vout\":2,\"prevout\":{\"value\":16390881}}],\"vout\":[{\"scriptpubkey_address\":\"1Kmrzq3WGCQsZw5kroEphuk1KgsEr65yB7\",\"value\":23858},{\"scriptpubkey_address\":\"bc1qyw5qql9m7rkse9mhcun225nrjpwycszsa5dpjg\",\"value\":7015000},{\"scriptpubkey_address\":\"bc1q90y3p6mg0pe3rvvzfeudq4mfxafgpc9rulruff\",\"value\":10774186}],\"size\":554,\"weight\":1559,\"fee\":41730,\"status\":{\"confirmed\":true,\"block_height\":668198}}",
|
||||
"b9e1a791f4091910caeb70d1a5d56452bc9614c16b5b74281b2485551faeb46e": "{\"txid\":\"b9e1a791f4091910caeb70d1a5d56452bc9614c16b5b74281b2485551faeb46e\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":1432}},{\"vout\":2,\"prevout\":{\"value\":30302311}}],\"vout\":[{\"scriptpubkey_address\":\"16MK64AGvKVF7xu9Xfjh8o7Xo4e1HMhUqq\",\"value\":1422},{\"scriptpubkey_address\":\"bc1qjp535w2zl3cxg02xgdx8yewtvn6twcnj86t73c\",\"value\":1600000},{\"scriptpubkey_address\":\"bc1qa58rfr0wumczmau0qehjwcsdkcgs5dmkg7url5\",\"value\":28698421}],\"size\":405,\"weight\":1287,\"fee\":3900,\"status\":{\"confirmed\":true,\"block_height\":666373}}",
|
||||
"dc06cd41f4b778553a0a5df4578c62eeb0c9c878b6f1a24c60b619a6749877c7": "{\"txid\":\"dc06cd41f4b778553a0a5df4578c62eeb0c9c878b6f1a24c60b619a6749877c7\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":230000000}}],\"vout\":[{\"scriptpubkey_address\":\"38bZBj5peYS3Husdz7AH3gEUiUbYRD951t\",\"value\":200000},{\"scriptpubkey_address\":\"bc1qaq3v7gjqaeyx7yzkcu59l8f47apkfutr927xa8\",\"value\":30000000},{\"scriptpubkey_address\":\"bc1qx9avgdnkal2jfcfjqdsdu7ly60awl4wcfgk6m0\",\"value\":199793875}],\"size\":255,\"weight\":690,\"fee\":6125,\"status\":{\"confirmed\":true,\"block_height\":668527}}",
|
||||
"ef1ea38b46402deb7df08c13a6dc379a65542a6940ac9d4ba436641ffd4bcb6e": "{\"txid\":\"ef1ea38b46402deb7df08c13a6dc379a65542a6940ac9d4ba436641ffd4bcb6e\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":61000}},{\"vout\":0,\"prevout\":{\"value\":6415500}}],\"vout\":[{\"scriptpubkey_address\":\"164hNDe95nNsQYVSVbeypn36HqT5uD5AoT\",\"value\":60908},{\"scriptpubkey_address\":\"1MEsN2jLyrcWBMjggSPs88xAnj6D38sQL3\",\"value\":2395500},{\"scriptpubkey_address\":\"1A3pYPW1zQcMpHUnSfPCxYWgCrUW93t2yV\",\"value\":3973352}],\"size\":408,\"weight\":1632,\"fee\":46740,\"status\":{\"confirmed\":true,\"block_height\":640441}}",
|
||||
"654a7a34321b57be6a553052d1d9e0f1764dd2fab7b64c9422e9953e4d9d127d": "{\"txid\":\"654a7a34321b57be6a553052d1d9e0f1764dd2fab7b64c9422e9953e4d9d127d\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":26000000}}],\"vout\":[{\"scriptpubkey_address\":\"18GzH11T5h2fpvUoBJDub7MgNJVw3FfqQ8\",\"value\":238000},{\"scriptpubkey_address\":\"1JYZ4cba5pjPxXqm5MDGUVvj2k3cZezRaR\",\"value\":3000000},{\"scriptpubkey_address\":\"12DNP86oaEXfEBkow4Kpkw2tNaqoECYhtc\",\"value\":22756800}],\"size\":260,\"weight\":1040,\"fee\":5200,\"status\":{\"confirmed\":true,\"block_height\":554950}}",
|
||||
"1c8e4934f93b5bbd2823318d5d491698316216f2e4bc0d7cd353f6b16358d80e": "{\"txid\":\"1c8e4934f93b5bbd2823318d5d491698316216f2e4bc0d7cd353f6b16358d80e\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":2,\"prevout\":{\"value\":563209}},{\"vout\":1,\"prevout\":{\"value\":600000}},{\"vout\":2,\"prevout\":{\"value\":214153}},{\"vout\":2,\"prevout\":{\"value\":116517}},{\"vout\":2,\"prevout\":{\"value\":135306}},{\"vout\":2,\"prevout\":{\"value\":261906}},{\"vout\":2,\"prevout\":{\"value\":598038}},{\"vout\":1,\"prevout\":{\"value\":600000}},{\"vout\":1,\"prevout\":{\"value\":600000}},{\"vout\":1,\"prevout\":{\"value\":600932}},{\"vout\":1,\"prevout\":{\"value\":600944}}],\"vout\":[{\"scriptpubkey_address\":\"19qA2BVPoyXDfHKVMovKG7SoxGY7xrBV8c\",\"value\":546},{\"scriptpubkey_address\":\"bc1qwcwu3mx0nmf290y8t0jlukhxujaul0fc4jxe44\",\"value\":4743164},{\"scriptpubkey_address\":\"bc1qduw8nd2sscyezk02xj3a3ks5adh8wmctqaew6g\",\"value\":145131}],\"size\":1746,\"weight\":3417,\"fee\":2164,\"status\":{\"confirmed\":true,\"block_height\":669227}}",
|
||||
"e125fdbd09ee86c01e16e1f12a31507cfb8703ed1bd5a221461adf33cb3e00d9": "{\"txid\":\"e125fdbd09ee86c01e16e1f12a31507cfb8703ed1bd5a221461adf33cb3e00d9\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":1,\"prevout\":{\"value\":4250960}}],\"vout\":[{\"scriptpubkey_address\":\"1PUXU1MQ82JC3Hx1NN5tZs3BaTAJVg72MC\",\"value\":200000},{\"scriptpubkey_address\":\"1MSkjSzF1dTKR121scX64Brvs4zhExVE8Q\",\"value\":4000000}],\"size\":225,\"weight\":900,\"fee\":50960,\"status\":{\"confirmed\":true,\"block_height\":578733}}",
|
||||
"051770f8d7f43a9b6ca10fefa6cdf4cb124a81eed26dc8af2e40f57d2589107b": "{\"txid\":\"051770f8d7f43a9b6ca10fefa6cdf4cb124a81eed26dc8af2e40f57d2589107b\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":0,\"prevout\":{\"value\":23985}},{\"vout\":1,\"prevout\":{\"value\":6271500}},{\"vout\":0,\"prevout\":{\"value\":2397000}},{\"vout\":2,\"prevout\":{\"value\":41281331}}],\"vout\":[{\"scriptpubkey_address\":\"16pULNutwpJ5E6EaxopQQDAVaFJXt8B18Z\",\"value\":23893},{\"scriptpubkey_address\":\"bc1q6hkhftt9v5kkcj9wr66ycqy23dqyle3h3wnv50\",\"value\":18365500},{\"scriptpubkey_address\":\"bc1q3ffqm4e4wxdg8jgcw0wlpw4vg9hgwnql3y9zn0\",\"value\":31546169}],\"size\":703,\"weight\":2476,\"fee\":38254,\"status\":{\"confirmed\":true,\"block_height\":667928}}",
|
||||
"72cabb5c323c923b43c7f6551974f591dcee148778ee34f9131011ea0ca82813": "{\"txid\":\"72cabb5c323c923b43c7f6551974f591dcee148778ee34f9131011ea0ca82813\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":1,\"prevout\":{\"value\":12000000}}],\"vout\":[{\"scriptpubkey_address\":\"3EfRGckBQQuk7cpU7SwatPv8kFD1vALkTU\",\"value\":546},{\"scriptpubkey_address\":\"bc1q6xthjqca0p83mua54e9t0sapxkvc7n3dvwssxc\",\"value\":2600000},{\"scriptpubkey_address\":\"bc1q3uaew9e6uqm6pth8nq7wh3wcwzxwh2q25fggcg\",\"value\":9388079}],\"size\":254,\"weight\":689,\"fee\":11375,\"status\":{\"confirmed\":true,\"block_height\":672972}}",
|
||||
"17cbd95d8809dc8808a5c209208f59c4a80e09e012e62951668d30d716c44a96": "{\"txid\":\"17cbd95d8809dc8808a5c209208f59c4a80e09e012e62951668d30d716c44a96\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":2,\"prevout\":{\"value\":2810563}}],\"vout\":[{\"scriptpubkey_address\":\"13sxMq8mTw7CTSqgGiMPfwo6ZDsVYrHLmR\",\"value\":546},{\"scriptpubkey_address\":\"bc1qklv4zsl598ujy2ntl5g3wqjxasu2f74egw0tlm\",\"value\":1603262},{\"scriptpubkey_address\":\"bc1qclesyfupj309620thesxmj4vcdscjfykdqz4np\",\"value\":1205124}],\"size\":256,\"weight\":697,\"fee\":1631,\"status\":{\"confirmed\":true,\"block_height\":669340}}",
|
||||
"cd99836ac4246c3e3980edf95773060481ce52271b74dadeb41e18c42ed21188": "{\"txid\":\"cd99836ac4246c3e3980edf95773060481ce52271b74dadeb41e18c42ed21188\",\"version\":1,\"locktime\":0,\"vin\":[{\"vout\":2,\"prevout\":{\"value\":200584}},{\"vout\":1,\"prevout\":{\"value\":600000}}],\"vout\":[{\"scriptpubkey_address\":\"bc1q7sd0k2a6p942848y5nsk9cqwdguhd7c04t2t3w\",\"value\":750000},{\"scriptpubkey_address\":\"bc1qrcez45uf02sg6zvk3mqmtlc9vnrvn50jcywlk5\",\"value\":49144}],\"size\":371,\"weight\":833,\"fee\":1440,\"status\":{\"confirmed\":true,\"block_height\":669442}}"
|
||||
}
|
9
core/src/test/resources/mainnet.seednodes
Normal file
9
core/src/test/resources/mainnet.seednodes
Normal file
|
@ -0,0 +1,9 @@
|
|||
# nodeaddress.onion:port [(@owner,@backup)]
|
||||
5quyxpxheyvzmb2d.onion:8000 (@miker)
|
||||
s67qglwhkgkyvr74.onion:8000 (@emzy)
|
||||
ef5qnzx6znifo3df.onion:8000 (@alexej996)
|
||||
jhgcy2won7xnslrb.onion:8000 (@wiz,@nicolasdorier)
|
||||
3f3cu2yw7u457ztq.onion:8000 (@devinbileck,@ripcurlx)
|
||||
723ljisnynbtdohi.onion:8000 (@emzy)
|
||||
rm7b56wbrcczpjvl.onion:8000 (@miker)
|
||||
fl3mmribyxgrv63c.onion:8000 (@devinbileck,@ripcurlx)
|
|
@ -0,0 +1 @@
|
|||
mock-maker-inline # enable mocking final classes in mockito
|
10
core/src/test/resources/regtest.seednodes
Normal file
10
core/src/test/resources/regtest.seednodes
Normal file
|
@ -0,0 +1,10 @@
|
|||
# For development you need to change that to your local onion addresses
|
||||
# 1. Run a seed node with prog args: --bitcoinNetwork=regtest --nodePort=8002 --appName=bisq_seed_node_rxdkppp3vicnbgqt.onion_8002
|
||||
# 2. Find your local onion address in bisq_seed_node_rxdkppp3vicnbgqt.onion_8002/regtest/tor/hiddenservice/hostname
|
||||
# 3. Shut down the seed node
|
||||
# 4. Rename the directory with your local onion address
|
||||
# 5. Edit here your found onion address (new NodeAddress("YOUR_ONION.onion:8002")
|
||||
|
||||
# nodeaddress.onion:port [(@owner)]
|
||||
rxdkppp3vicnbgqt.onion:8002
|
||||
4ie52dse64kaarxw.onion:8002
|
7
core/src/test/resources/testnet.seednodes
Normal file
7
core/src/test/resources/testnet.seednodes
Normal file
|
@ -0,0 +1,7 @@
|
|||
# nodeaddress.onion:port [(@owner)]
|
||||
snenz4mea65wigen.onion:8001
|
||||
fjr5w4eckjghqtnu.onion:8001
|
||||
3d56s6acbi3vk52v.onion:8001
|
||||
74w2sttlo4qk6go3.onion:8001
|
||||
gtif46mfxirv533z.onion:8001
|
||||
jmc5ajqvtnzqaggm.onion:8001
|
Loading…
Add table
Add a link
Reference in a new issue