All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tbk.spring.testcontainer.bitcoind.config.BitcoindContainerProperties Maven / Gradle / Ivy

The newest version!
package org.tbk.spring.testcontainer.bitcoind.config;

import com.google.common.annotations.Beta;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import lombok.*;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.tbk.spring.testcontainer.core.AbstractContainerProperties;
import org.testcontainers.shaded.com.google.common.base.CharMatcher;
import org.testcontainers.utility.DockerImageName;

import java.util.List;
import java.util.Optional;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * Properties for bitcoind testcontainer.
 *
 * 

Mainnet * JSON-RPC/REST: 8332 * P2P: 8333 * *

Testnet * Testnet JSON-RPC: 18332 * P2P: 18333 * *

Regtest * JSON-RPC/REST: 18443 (since 0.16+, otherwise 18332) * P2P: 18444 */ @Data @EqualsAndHashCode(callSuper = false) @ConfigurationProperties( prefix = "org.tbk.spring.testcontainer.bitcoind", ignoreUnknownFields = false ) public class BitcoindContainerProperties extends AbstractContainerProperties implements Validator { private static final String DEFAULT_DOCKER_IMAGE_NAME = "polarlightning/bitcoind:27.0"; private static final DockerImageName defaultDockerImageName = DockerImageName.parse(DEFAULT_DOCKER_IMAGE_NAME); private static final int MAINNET_DEFAULT_RPC_PORT = 8332; private static final int MAINNET_DEFAULT_P2P_PORT = 8333; private static final int TESTNET_DEFAULT_RPC_PORT = 18332; private static final int TESTNET_DEFAULT_P2P_PORT = 18333; private static final int REGTEST_DEFAULT_RPC_PORT = 18443; private static final int REGTEST_DEFAULT_P2P_PORT = 18444; private static final Network DEFAULT_NETWORK = Network.regtest; @Beta private static final List reservedCommands = ImmutableList.builder() .add("rpcuser") .add("rpcpassword") .add("rpcport") .add("port") .add("chain") .add("testnet") .add("regtest") .add("signet") .build(); /** * See chainparamsbase.cpp. */ @Getter @RequiredArgsConstructor public enum Network { mainnet("main"), testnet("test"), regtest("regtest"); @NonNull private final String chain; } public BitcoindContainerProperties() { super(defaultDockerImageName, reservedCommands); } private Network network; /** * RPC port */ private Integer rpcport; /** * P2P Port */ private Integer port; /** * RPC username. */ private String rpcuser; /** * RPC password. */ private String rpcpassword; public Network getNetwork() { return this.network != null ? this.network : DEFAULT_NETWORK; } public Optional getRpcuser() { return Optional.ofNullable(rpcuser); } public Optional getRpcpassword() { return Optional.ofNullable(rpcpassword); } public int getRpcport() { return rpcport != null ? rpcport : switch (getNetwork()) { case mainnet -> MAINNET_DEFAULT_RPC_PORT; case testnet -> TESTNET_DEFAULT_RPC_PORT; case regtest -> REGTEST_DEFAULT_RPC_PORT; }; } public int getPort() { return port != null ? port : switch (getNetwork()) { case mainnet -> MAINNET_DEFAULT_P2P_PORT; case testnet -> TESTNET_DEFAULT_P2P_PORT; case regtest -> REGTEST_DEFAULT_P2P_PORT; }; } public Optional getCommandValueByKey(String key) { String commandWithPrefix = "-" + key; return this.getCommands().stream() .filter(it -> it.startsWith(commandWithPrefix)) .map(it -> { boolean withoutValue = commandWithPrefix.length() == it.length(); if (withoutValue) { return ""; } checkArgument('=' == it.charAt(commandWithPrefix.length())); return it.split(commandWithPrefix + "=", 2)[1]; }) .findFirst(); } @Override public boolean supports(Class clazz) { return clazz == BitcoindContainerProperties.class; } /** * Validate the container properties. * *

Keep in mind that Testcontainers splits commands on whitespaces. * This means, every property that is part of a command, must not contain whitespaces. * {@inheritDoc} */ @Override public void validate(Object target, Errors errors) { BitcoindContainerProperties properties = (BitcoindContainerProperties) target; String rpcuserValue = properties.getRpcuser().orElse(null); if (rpcuserValue != null) { if (rpcuserValue.isBlank()) { String errorMessage = "'rpcuser' must not be empty"; errors.rejectValue("rpcuser", "rpcuser.invalid", errorMessage); } else if (containsWhitespaces(rpcuserValue)) { String errorMessage = String.format("'rpcuser' must not contain whitespaces - unsupported value: '%s'", rpcuserValue); errors.rejectValue("rpcuser", "rpcuser.unsupported", errorMessage); } } String rpcpasswordValue = properties.getRpcpassword().orElse(null); if (rpcpasswordValue != null) { if (rpcpasswordValue.isBlank()) { String errorMessage = "'rpcpassword' must not be empty"; errors.rejectValue("rpcpassword", "rpcpassword.invalid", errorMessage); } else if (containsWhitespaces(rpcpasswordValue)) { String errorMessage = "'rpcpassword' must not contain whitespaces - unsupported value"; errors.rejectValue("rpcpassword", "rpcpassword.unsupported", errorMessage); } } int rpcportValue = properties.getRpcport(); if (!isValidPort(rpcportValue)) { String errorMessage = "'rpcport' must be in the range 0-65535"; errors.rejectValue("rpcport", "rpcport.invalid", errorMessage); } int p2pport = properties.getPort(); if (!isValidPort(p2pport)) { String errorMessage = "'port' must be in the range 0-65535"; errors.rejectValue("port", "port.invalid", errorMessage); } reservedCommands.stream() .filter(val -> properties.getCommandValueByKey(val).isPresent()) .forEach(reservedKey -> { String errorMessage = String.format("'commands' entry must not contain key '%s'", reservedKey); errors.rejectValue("commands", "commands.reserved", errorMessage); }); for (String command : properties.getCommands()) { if (Strings.isNullOrEmpty(command)) { String errorMessage = "'commands' entry must not be empty"; errors.rejectValue("commands", "commands.invalid", errorMessage); } else { boolean startsWithHyphen = command.startsWith("-"); if (!startsWithHyphen) { String errorMessage = String.format( "'commands' entry must start with '-': invalid value: %s", command ); errors.rejectValue("commands", "commands.invalid", errorMessage); } if (containsWhitespaces(command)) { String errorMessage = String.format( "'commands' entry must not contain whitespaces: unsupported value: %s", command ); errors.rejectValue("commands", "commands.unsupported", errorMessage); } } } } private static boolean containsWhitespaces(String value) { return CharMatcher.whitespace().matchesAnyOf(value); } private static boolean isValidPort(int port) { return port >= 0 && port <= 65535; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy