Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ CoinbaseRewardAddressSupplier bitcoinClientCoinbaseRewardAddressSupplier(Bitcoin
@ConditionalOnMissingBean
RegtestMiner regtestMiner(BitcoinClient bitcoinJsonRpcClient,
CoinbaseRewardAddressSupplier coinbaseRewardAddressSupplier) {
Duration serverTimeout = properties.getServerTimeout();
return new RegtestMinerImpl(bitcoinJsonRpcClient, coinbaseRewardAddressSupplier);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
@Getter
@AllArgsConstructor(onConstructor = @__(@ConstructorBinding))
public class BitcoinRegtestMiningProperties implements Validator {
private static final Duration DEFAULT_SERVER_TIMEOUT = Duration.ofSeconds(30);
/**
* Whether mining should be enabled.
*/
Expand All @@ -24,6 +25,8 @@ public class BitcoinRegtestMiningProperties implements Validator {
*/
private String coinbaseRewardAddress;

private Duration serverTimeout;

private int mineInitialAmountOfBlocks;

private Boolean scheduledMiningEnabled;
Expand All @@ -34,6 +37,10 @@ public Optional<String> getCoinbaseRewardAddress() {
return Optional.ofNullable(coinbaseRewardAddress);
}

public Duration getServerTimeout() {
return serverTimeout != null ? serverTimeout : DEFAULT_SERVER_TIMEOUT;
}

public NextBlockDurationProperties getNextBlockDuration() {
return nextBlockDuration != null ? nextBlockDuration : new NextBlockDurationProperties(null, null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ ElectrumRegtestFaucet electrumRegtestFaucet(BitcoinjElectrumClient electrumClien
);
}

@SuppressFBWarnings("HARD_CODE_PASSWORD") // okay for a regtest faucet
@SuppressFBWarnings(value = "HARD_CODE_PASSWORD", justification = "okay for a regtest faucet")
WalletParams faucetWalletParams() {
String pseudoRandomPostfix = UUID.randomUUID().toString().substring(0, 8);
String walletName = "faucet_%d_%s".formatted(Instant.now().toEpochMilli(), pseudoRandomPostfix);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,35 @@
import org.consensusj.bitcoin.jsonrpc.BitcoinClient;

import java.io.IOException;
import java.time.Duration;
import java.util.List;

import static java.util.Objects.requireNonNull;

@Slf4j
public final class RegtestMinerImpl implements RegtestMiner {
private static final Duration DEFAULT_SERVER_TIMEOUT = Duration.ofSeconds(10);

private final BitcoinClient client;
private final CoinbaseRewardAddressSupplier coinbaseRewardAddressSupplier;
private final Duration serverTimeout;

public RegtestMinerImpl(BitcoinClient client) {
this(client, new RegtestEaterAddressSupplier());
}

@SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "class from external dependency")
public RegtestMinerImpl(BitcoinClient client, CoinbaseRewardAddressSupplier coinbaseRewardAddressSupplier) {
this(client, coinbaseRewardAddressSupplier, DEFAULT_SERVER_TIMEOUT);
}

@SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "class from external dependency")
public RegtestMinerImpl(BitcoinClient client,
CoinbaseRewardAddressSupplier coinbaseRewardAddressSupplier,
Duration serverTimeout) {
this.client = requireNonNull(client);
this.coinbaseRewardAddressSupplier = requireNonNull(coinbaseRewardAddressSupplier);
this.serverTimeout = requireNonNull(serverTimeout);
}

@Override
Expand All @@ -41,9 +52,11 @@ public List<Sha256Hash> mineBlocks(int count, CoinbaseRewardAddressSupplier addr

log.debug("Trying to mine {} block(s) with coinbase reward for address {}", count, coinbaseRewardAddress);

this.client.waitForServer((int) serverTimeout.toSeconds());

blockHashes.addAll(this.client.generateToAddress(count, coinbaseRewardAddress));
while (blockHashes.size() < count) {
// might have mined less blocks than requested, mine till requested amount is reached
// might have mined fewer blocks than requested, mine till requested amount is reached
blockHashes.addAll(this.client.generateToAddress(1, coinbaseRewardAddress));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

@Slf4j
public final class AwaitTransactionAction implements RegtestAction<OnchainHistory.Transaction> {
private static final Duration defaultTimeout = Duration.ofSeconds(30);
private static final Duration defaultTimeout = Duration.ofSeconds(60);
private static final Duration defaultCheckInterval = Duration.ofMillis(100);
private static final int DEFAULT_CONFIRMATIONS = 0;

Expand Down
2 changes: 1 addition & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Breaking
- electrum: moved electrum RPC related code to distinct package
- electrum: compatibility wit electrum v4.6.0
- electrum: upgrade electrum testcontainer from v3.3.8 to v4.6.1
- electrum: upgrade electrum testcontainer from v3.3.8 to v4.6.2

### Changed
- upgrade: update tor testcontainer from v0.4.7.8 to v0.4.8.10
Expand Down
2 changes: 1 addition & 1 deletion docker/regtest/electrum/data/electrum/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
FROM ghcr.io/theborakompanioni/electrum-daemon:4.6.1@sha256:39b74e348ac0644e6b2bb5d12ced3d98e96d6929cac8383b9c149aa1beb916ae
FROM ghcr.io/theborakompanioni/electrum-daemon:4.6.2@sha256:b214fa9a30cb260a99daa88a5880abf110bda1556a359a1706d3e2112e4b088a

COPY --chown=electrum:electrum ./regtest/wallets /home/electrum/.electrum/regtest/wallets
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ void verifyTargetWalletReceivesCoins() {
@ActiveProfiles("test")
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(ElectrumxContainerAutoConfiguration.class)
@SuppressFBWarnings("HARD_CODE_PASSWORD") // okay for this test
@SuppressFBWarnings(value = "HARD_CODE_PASSWORD", justification = "okay for this test")
public static class TestConfig {
private static final String TEST_ELECTRUM_RPCPASSWORD1 = "correct_horse_battery_staple_20210516-0";
private static final String TEST_ELECTRUM_RPCPASSWORD2 = "correct_horse_battery_staple_20210516-1";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ final class LnurlAuthSessionRedirectStrategy extends DefaultRedirectStrategy {
}

@Override
@SuppressFBWarnings("XSS_SERVLET") // false positive
@SuppressFBWarnings(value = "XSS_SERVLET", justification = "false positive")
public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException {
boolean clientPrefersJson = clientPrefersJson(request);
if (!clientPrefersJson) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ private void doFilter(HttpServletRequest request, HttpServletResponse response,
chain.doFilter(request, response);
}

@SuppressFBWarnings("XSS_SERVLET") // false positive
@SuppressFBWarnings(value = "XSS_SERVLET", justification = "false positive")
private void writeLoginStylesheet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String script = this.loginPageGenerator.createStylesheet();

Expand All @@ -148,7 +148,7 @@ private void writeLoginStylesheet(HttpServletRequest request, HttpServletRespons
response.getWriter().write(script);
}

@SuppressFBWarnings("XSS_SERVLET") // false positive
@SuppressFBWarnings(value = "XSS_SERVLET", justification = "false positive")
private void writeLoginScript(HttpServletRequest request, HttpServletResponse response) throws IOException {
// prevent logged-in users from invoking browser session migration - this would log out the user
// as an AuthenticationException is thrown, which invalidates the user's authentication.
Expand Down Expand Up @@ -206,7 +206,7 @@ private void writeLoginErrorPage(HttpServletRequest request, HttpServletResponse
writeHtml(request, response, content);
}*/

@SuppressFBWarnings("XSS_SERVLET") // false positive
@SuppressFBWarnings(value = "XSS_SERVLET", justification = "false positive")
private void writeHtml(HttpServletRequest request, HttpServletResponse response, String content) throws IOException {
response.setContentType(HTML_CONTENT_TYPE);
response.setContentLength(content.getBytes(StandardCharsets.UTF_8).length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,14 @@ protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServle
}
}

@SuppressFBWarnings("XSS_SERVLET") // false positive - a hardcoded value is written
@SuppressFBWarnings(value = "XSS_SERVLET", justification = "false positive - a hardcoded value is writtene")
protected void writeSuccessBody(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write(successBody);
}

@SuppressFBWarnings("XSS_SERVLET") // false positive - a hardcoded value is written
@SuppressFBWarnings(value = "XSS_SERVLET", justification = "false positive - a hardcoded value is written")
protected void writeErrorBody(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils;

import java.time.Duration;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -515,6 +516,43 @@ void testOnchainHistory() {
assertThat(history.getTransactions(), is(hasSize(greaterThanOrEqualTo(0))));
}

@Test
void testOnchainHistoryWithParamsHeight() {
OnchainHistory history = sut.getOnchainHistory(OnchainHistoryParams.builder()
.walletPath(defaultWalletParams.getWalletPath())
.fromHeight(0L)
.toHeight(100_000_000L)
.build());

assertThat(history.getTransactions(), is(hasSize(greaterThanOrEqualTo(0))));
}

@Test
void testOnchainHistoryWithParamsYear() {
OnchainHistory history = sut.getOnchainHistory(OnchainHistoryParams.builder()
.walletPath(defaultWalletParams.getWalletPath())
.year(LocalDate.now().getYear())
.build());

assertThat(history.getTransactions(), is(hasSize(greaterThanOrEqualTo(0))));
}

@Test
void testOnchainHistoryWithParamsError() {
JsonRpcException e = Assertions.assertThrows(JsonRpcException.class, () -> {
sut.getOnchainHistory(OnchainHistoryParams.builder()
.year(LocalDate.now().getYear())
.fromHeight(0L)
.toHeight(100_000_000L)
.walletPath(defaultWalletParams.getWalletPath())
.build());
});

ErrorMessage error = e.getErrorMessage();
assertThat(error.getMessage(), is("timestamp and block height based filtering cannot be used together"));
assertThat(error.getCode(), is(1));
}

@Test
void testOnchainCapitalGains() {
OnchainSummary summary = sut.getOnchainCapitalGains(OnchainCapitalGainsParams.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ void testPayto() throws Exception {

// Check that the transaction is in the onchain history
OnchainHistory history = sut.getOnchainHistory(OnchainHistoryParams.builder()
.fromHeight(1L)
.toHeight(100_000_000L)
.walletPath(defaultWalletParams.getWalletPath())
.build());
OnchainHistory.Transaction tx = history.getTransactions().stream()
Expand Down Expand Up @@ -279,11 +281,12 @@ void testDeserializeTransactionFromCoinbase() {
assertThat(blocks, hasSize(1));

Balance balanceAfter = Flux.interval(Duration.ofMillis(100))
.map(it -> sut.waitForWalletSynchronization(defaultWalletParams))
.map(it -> sut.getBalance(GetBalanceParams.builder()
.walletPath(defaultWalletParams.getWalletPath())
.build()))
.filter(it -> it.getTotal().getValue() > balanceBefore.getTotal().getValue())
.blockFirst(Duration.ofSeconds(30));
.blockFirst(Duration.ofSeconds(60));
assertThat(balanceAfter, is(notNullValue()));
assertThat(balanceAfter.getUnmatured().getValue(), is(greaterThan(balanceBefore.getUnmatured().getValue())));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@

logging.level.org.tbk.bitcoin.regtest: DEBUG

org.tbk.spring.testcontainer.electrum-daemon:
enabled: true
default-wallet:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ public OnchainHistory getOnchainHistory(OnchainHistoryParams params) {
true,
params.getYear(),
false,
params.getFromHeight(),
params.getToHeight(),
params.getWalletPath()
);
return SimpleOnchainHistory.from(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,10 +477,23 @@ OnchainCapitalGainsResponse onchaincapitalgains(
@JsonRpcMethod("createnewaddress")
String createnewaddress(@JsonRpcOptional @JsonRpcParam("wallet_path") String walletPath);

/**
* Wallet onchain history. Returns the transaction history of your wallet.
*
* @param showAddresses Show input and output addresses
* @param year Show history for a given year
* @param showFiat Show fiat value of transactions
* @param fromHeight Only show transactions that confirmed after(inclusive) given block height
* @param toHeight Only show transactions that confirmed before(exclusive) given block height
* @param walletPath wallet path
* @return transaction history of your wallet
*/
@JsonRpcMethod("onchain_history")
List<OnchainHistoryResponse.HistoricTransaction> onchainhistory(@JsonRpcOptional @JsonRpcParam("show_addresses") Boolean showAddresses,
@JsonRpcOptional @JsonRpcParam("year") Long year,
@JsonRpcOptional @JsonRpcParam("year") Integer year,
@JsonRpcOptional @JsonRpcParam("show_fiat") Boolean showFiat,
@JsonRpcOptional @JsonRpcParam("from_height") Long fromHeight,
@JsonRpcOptional @JsonRpcParam("to_height") Long toHeight,
@JsonRpcOptional @JsonRpcParam("wallet_path") String walletPath);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@ public class OnchainHistoryParams {

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonProperty("year")
Long year;
Integer year;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonProperty("from_height")
Long fromHeight;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonProperty("to_height")
Long toHeight;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonProperty("wallet_path")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
public class ElectrumDaemonContainerConfig {

// currently only the image from "theborakompanioni" is supported
static final String DEFAULT_DOCKER_IMAGE_NAME = "ghcr.io/theborakompanioni/electrum-daemon:4.6.1@sha256:39b74e348ac0644e6b2bb5d12ced3d98e96d6929cac8383b9c149aa1beb916ae";
static final String DEFAULT_DOCKER_IMAGE_NAME = "ghcr.io/theborakompanioni/electrum-daemon:4.6.2@sha256:b214fa9a30cb260a99daa88a5880abf110bda1556a359a1706d3e2112e4b088a";
static final DockerImageName defaultDockerImageName = DockerImageName.parse(DEFAULT_DOCKER_IMAGE_NAME);

private static final Map<String, String> defaultEnvironment = ImmutableMap.<String, String>builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ void contextLoads() {
}

@Test
@SuppressFBWarnings("URLCONNECTION_SSRF_FD")
// we are in control of the request
@SuppressFBWarnings(value = "URLCONNECTION_SSRF_FD", justification = "we are in control of the request")
void fetchPageWithTor() throws IOException {
SocketAddress sockAddr = new InetSocketAddress("localhost", container.getMappedPort(9050));
Proxy proxy = new Proxy(Proxy.Type.SOCKS, sockAddr);
Expand Down
Loading