mirror of
https://github.com/aljazceru/lnd-manageJ.git
synced 2026-01-20 06:24:28 +01:00
allow users to specify resolutions manually
This commit is contained in:
10
hardcoded/build.gradle
Normal file
10
hardcoded/build.gradle
Normal file
@@ -0,0 +1,10 @@
|
||||
plugins {
|
||||
id 'lnd-manageJ.java-library-conventions'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':model')
|
||||
implementation project(':caching')
|
||||
implementation 'org.ini4j:ini4j:0.5.4'
|
||||
testImplementation testFixtures(project(':model'))
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package de.cotto.lndmanagej.hardcoded;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import de.cotto.lndmanagej.model.ChannelId;
|
||||
import de.cotto.lndmanagej.model.Resolution;
|
||||
import de.cotto.lndmanagej.model.TransactionHash;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
@Component
|
||||
public class HardcodedService {
|
||||
private static final int EXPECTED_NUMBER_OF_COMPONENTS = 3;
|
||||
private static final String RESOLUTIONS_SECTION = "resolutions";
|
||||
private static final Splitter SPLITTER = Splitter.on(":");
|
||||
|
||||
private final IniFileReader iniFileReader;
|
||||
|
||||
public HardcodedService(IniFileReader iniFileReader) {
|
||||
this.iniFileReader = iniFileReader;
|
||||
}
|
||||
|
||||
public Set<Resolution> getResolutions(ChannelId channelId) {
|
||||
Map<String, Set<String>> values = iniFileReader.getValues(RESOLUTIONS_SECTION);
|
||||
Set<String> forShortChannelId = values.getOrDefault(String.valueOf(channelId.getShortChannelId()), Set.of());
|
||||
Set<String> forCompactForm = values.getOrDefault(channelId.getCompactForm(), Set.of());
|
||||
Set<String> forCompactFormLnd = values.getOrDefault(channelId.getCompactFormLnd(), Set.of());
|
||||
return Stream.of(forShortChannelId, forCompactForm, forCompactFormLnd)
|
||||
.flatMap(Set::stream)
|
||||
.map(this::parseResolution)
|
||||
.flatMap(Optional::stream)
|
||||
.collect(toSet());
|
||||
}
|
||||
|
||||
private Optional<Resolution> parseResolution(String encodedResolution) {
|
||||
try {
|
||||
List<String> split = SPLITTER.splitToList(encodedResolution);
|
||||
if (split.size() != EXPECTED_NUMBER_OF_COMPONENTS) {
|
||||
return Optional.empty();
|
||||
}
|
||||
String resolutionType = split.get(0);
|
||||
String outcome = split.get(1);
|
||||
TransactionHash sweepTransaction = TransactionHash.create(split.get(2));
|
||||
return Optional.of(new Resolution(Optional.of(sweepTransaction), resolutionType, outcome));
|
||||
} catch (IllegalArgumentException exception) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package de.cotto.lndmanagej.hardcoded;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import de.cotto.lndmanagej.caching.CacheBuilder;
|
||||
import org.ini4j.Ini;
|
||||
import org.ini4j.Profile;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
public class IniFileReader {
|
||||
private final String path;
|
||||
private final LoadingCache<String, Map<String, Set<String>>> cache;
|
||||
|
||||
public IniFileReader(@Value("${lndmanagej.hardcoded-path:}") String path) {
|
||||
this.path = path;
|
||||
cache = new CacheBuilder()
|
||||
.withRefresh(Duration.ofSeconds(5))
|
||||
.withExpiry(Duration.ofSeconds(10))
|
||||
.build(this::getValuesWithoutCache);
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getValues(String sectionName) {
|
||||
return cache.get(sectionName);
|
||||
}
|
||||
|
||||
private Map<String, Set<String>> getValuesWithoutCache(String sectionName) {
|
||||
return getIni().map(ini -> ini.get(sectionName))
|
||||
.map(this::toMultiValueMap)
|
||||
.orElse(Map.of());
|
||||
}
|
||||
|
||||
private Map<String, Set<String>> toMultiValueMap(Profile.Section section) {
|
||||
LinkedHashMap<String, Set<String>> result = new LinkedHashMap<>();
|
||||
for (String key : section.keySet()) {
|
||||
result.put(key, new HashSet<>(section.getAll(key)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Optional<Ini> getIni() {
|
||||
try {
|
||||
return Optional.of(new Ini(new File(path)));
|
||||
} catch (IOException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package de.cotto.lndmanagej.hardcoded;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static de.cotto.lndmanagej.model.ChannelIdFixtures.CHANNEL_ID;
|
||||
import static de.cotto.lndmanagej.model.ResolutionFixtures.ANCHOR_CLAIMED;
|
||||
import static de.cotto.lndmanagej.model.ResolutionFixtures.COMMIT_CLAIMED;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class HardcodedServiceTest {
|
||||
private static final String SECTION = "resolutions";
|
||||
private static final String COMMIT_CLAIMED_STRING
|
||||
= "COMMIT:CLAIMED:abc222abc000abc000abc000abc000abc000abc000abc000abc000abc000abc0";
|
||||
private static final String ANCHOR_CLAIMED_STRING
|
||||
= "ANCHOR:CLAIMED:abc222abc000abc000abc000abc000abc000abc000abc000abc000abc000abc0";
|
||||
|
||||
@InjectMocks
|
||||
private HardcodedService hardcodedService;
|
||||
|
||||
@Mock
|
||||
private IniFileReader iniFileReader;
|
||||
|
||||
@Test
|
||||
void getResolutions_empty() {
|
||||
when(iniFileReader.getValues(SECTION)).thenReturn(Map.of());
|
||||
assertThat(hardcodedService.getResolutions(CHANNEL_ID)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResolutions_one_resolution_short_channel_id() {
|
||||
when(iniFileReader.getValues(SECTION))
|
||||
.thenReturn(Map.of(String.valueOf(CHANNEL_ID.getShortChannelId()), Set.of(COMMIT_CLAIMED_STRING)));
|
||||
assertThat(hardcodedService.getResolutions(CHANNEL_ID)).containsExactly(COMMIT_CLAIMED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResolutions_one_resolution_compact_form() {
|
||||
when(iniFileReader.getValues(SECTION))
|
||||
.thenReturn(Map.of(CHANNEL_ID.getCompactForm(), Set.of(COMMIT_CLAIMED_STRING)));
|
||||
assertThat(hardcodedService.getResolutions(CHANNEL_ID)).containsExactly(COMMIT_CLAIMED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResolutions_one_resolution_compact_form_lnd() {
|
||||
when(iniFileReader.getValues(SECTION))
|
||||
.thenReturn(Map.of(CHANNEL_ID.getCompactFormLnd(), Set.of(COMMIT_CLAIMED_STRING)));
|
||||
assertThat(hardcodedService.getResolutions(CHANNEL_ID)).containsExactly(COMMIT_CLAIMED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResolutions_two_resolutions() {
|
||||
when(iniFileReader.getValues(SECTION)).thenReturn(Map.of(
|
||||
CHANNEL_ID.getCompactFormLnd(),
|
||||
Set.of(COMMIT_CLAIMED_STRING, ANCHOR_CLAIMED_STRING)
|
||||
));
|
||||
assertThat(hardcodedService.getResolutions(CHANNEL_ID))
|
||||
.containsExactlyInAnyOrder(COMMIT_CLAIMED, ANCHOR_CLAIMED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResolutions_bogus_string() {
|
||||
when(iniFileReader.getValues(SECTION)).thenReturn(Map.of(
|
||||
CHANNEL_ID.getCompactFormLnd(),
|
||||
Set.of("hello", "hello:peter", "a:b:c")
|
||||
));
|
||||
assertThat(hardcodedService.getResolutions(CHANNEL_ID)).isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package de.cotto.lndmanagej.hardcoded;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class IniFileReaderTest {
|
||||
|
||||
private static final String SECTION = "section";
|
||||
private static final String SECTION_2 = "another-section";
|
||||
|
||||
@Test
|
||||
void file_does_not_exist() throws IOException {
|
||||
File file = createTempFile();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.delete();
|
||||
IniFileReader iniFileReader = new IniFileReader(file.getPath());
|
||||
assertThat(iniFileReader.getValues(SECTION)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void path_does_not_exist() {
|
||||
IniFileReader iniFileReader = new IniFileReader("/blabla/this/does/not/exist/foo.conf");
|
||||
assertThat(iniFileReader.getValues(SECTION)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void empty_file() throws IOException {
|
||||
File file = createTempFile();
|
||||
IniFileReader iniFileReader = new IniFileReader(file.getPath());
|
||||
assertThat(iniFileReader.getValues(SECTION)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void section_without_values() throws IOException {
|
||||
File file = createTempFile();
|
||||
addLineToFile(file, "[" + SECTION + "]");
|
||||
IniFileReader iniFileReader = new IniFileReader(file.getPath());
|
||||
assertThat(iniFileReader.getValues(SECTION)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void section_with_value() throws IOException {
|
||||
File file = createTempFile();
|
||||
addLineToFile(file, "[" + SECTION + "]", "x=y");
|
||||
IniFileReader iniFileReader = new IniFileReader(file.getPath());
|
||||
assertThat(iniFileReader.getValues(SECTION)).isEqualTo(Map.of("x", Set.of("y")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void section_with_two_values() throws IOException {
|
||||
File file = createTempFile();
|
||||
addLineToFile(file, "[" + SECTION + "]", "x=y", "a=b");
|
||||
IniFileReader iniFileReader = new IniFileReader(file.getPath());
|
||||
assertThat(iniFileReader.getValues(SECTION)).isEqualTo(Map.of("x", Set.of("y"), "a", Set.of("b")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void two_sections_with_two_values() throws IOException {
|
||||
File file = createTempFile();
|
||||
addLineToFile(file, "[" + SECTION + "]", "x=y", "a=b", "[" + SECTION_2 + "]", "x=1", "a=2");
|
||||
IniFileReader iniFileReader = new IniFileReader(file.getPath());
|
||||
assertThat(iniFileReader.getValues(SECTION)).isEqualTo(Map.of("x", Set.of("y"), "a", Set.of("b")));
|
||||
assertThat(iniFileReader.getValues(SECTION_2)).isEqualTo(Map.of("x", Set.of("1"), "a", Set.of("2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void section_with_two_values_for_key() throws IOException {
|
||||
File file = createTempFile();
|
||||
addLineToFile(file, "[" + SECTION + "]", "a=y", "a=b");
|
||||
IniFileReader iniFileReader = new IniFileReader(file.getPath());
|
||||
assertThat(iniFileReader.getValues(SECTION)).isEqualTo(Map.of("a", Set.of("y", "b")));
|
||||
}
|
||||
|
||||
private void addLineToFile(File file, String... lines) throws IOException {
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(file.toPath())) {
|
||||
for (String line : lines) {
|
||||
writer.write(line + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private File createTempFile() throws IOException {
|
||||
return File.createTempFile("hardcoded", "temp");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user