mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
fetch transaction history
This commit is contained in:
parent
c115f6e729
commit
331b83f7a0
8 changed files with 466 additions and 2 deletions
|
@ -33,6 +33,7 @@ dependencies {
|
||||||
implementation('org.fxmisc.richtext:richtextfx:0.10.4')
|
implementation('org.fxmisc.richtext:richtextfx:0.10.4')
|
||||||
implementation('no.tornado:tornadofx-controls:1.0.4')
|
implementation('no.tornado:tornadofx-controls:1.0.4')
|
||||||
implementation('com.google.zxing:javase:3.4.0')
|
implementation('com.google.zxing:javase:3.4.0')
|
||||||
|
implementation('com.github.arteam:simple-json-rpc-client:1.0')
|
||||||
implementation('org.controlsfx:controlsfx:11.0.1' ) {
|
implementation('org.controlsfx:controlsfx:11.0.1' ) {
|
||||||
exclude group: 'org.openjfx', module: 'javafx-base'
|
exclude group: 'org.openjfx', module: 'javafx-base'
|
||||||
exclude group: 'org.openjfx', module: 'javafx-graphics'
|
exclude group: 'org.openjfx', module: 'javafx-graphics'
|
||||||
|
|
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit 11978e1f48851cd964f1c5f52a29a8e2ea432432
|
Subproject commit a32410b53831b3e5b111dd8fc82291c7ab30c732
|
|
@ -382,6 +382,17 @@ public class AppController implements Initializable {
|
||||||
WalletForm walletForm = new WalletForm(storage, wallet);
|
WalletForm walletForm = new WalletForm(storage, wallet);
|
||||||
controller.setWalletForm(walletForm);
|
controller.setWalletForm(walletForm);
|
||||||
|
|
||||||
|
if(wallet.isValid()) {
|
||||||
|
ElectrumServer.TransactionHistoryService historyService = new ElectrumServer.TransactionHistoryService(wallet);
|
||||||
|
historyService.setOnSucceeded(workerStateEvent -> {
|
||||||
|
//TODO: Show connected
|
||||||
|
});
|
||||||
|
historyService.setOnFailed(workerStateEvent -> {
|
||||||
|
//TODO: Show not connected, log exception
|
||||||
|
});
|
||||||
|
historyService.start();
|
||||||
|
}
|
||||||
|
|
||||||
if(!storage.getWalletFile().exists() || wallet.containsSource(KeystoreSource.HW_USB)) {
|
if(!storage.getWalletFile().exists() || wallet.containsSource(KeystoreSource.HW_USB)) {
|
||||||
Hwi.ScheduledEnumerateService enumerateService = new Hwi.ScheduledEnumerateService(null);
|
Hwi.ScheduledEnumerateService enumerateService = new Hwi.ScheduledEnumerateService(null);
|
||||||
enumerateService.setPeriod(new Duration(30 * 1000));
|
enumerateService.setPeriod(new Duration(30 * 1000));
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.sparrowwallet.sparrow.io;
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import com.google.gson.*;
|
import com.google.gson.*;
|
||||||
import com.sparrowwallet.drongo.Utils;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
@ -11,6 +10,8 @@ public class Config {
|
||||||
|
|
||||||
private Integer keyDerivationPeriod;
|
private Integer keyDerivationPeriod;
|
||||||
private File hwi;
|
private File hwi;
|
||||||
|
private String electrumServer;
|
||||||
|
private File electrumServerCert;
|
||||||
|
|
||||||
private static Config INSTANCE;
|
private static Config INSTANCE;
|
||||||
|
|
||||||
|
@ -71,6 +72,24 @@ public class Config {
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getElectrumServer() {
|
||||||
|
return electrumServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setElectrumServer(String electrumServer) {
|
||||||
|
this.electrumServer = electrumServer;
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getElectrumServerCert() {
|
||||||
|
return electrumServerCert;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setElectrumServerCert(File electrumServerCert) {
|
||||||
|
this.electrumServerCert = electrumServerCert;
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
|
||||||
private void flush() {
|
private void flush() {
|
||||||
Gson gson = getGson();
|
Gson gson = getGson();
|
||||||
try {
|
try {
|
||||||
|
|
379
src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java
Normal file
379
src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java
Normal file
|
@ -0,0 +1,379 @@
|
||||||
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
|
import com.github.arteam.simplejsonrpc.client.*;
|
||||||
|
import com.github.arteam.simplejsonrpc.client.builder.BatchRequestBuilder;
|
||||||
|
import com.github.arteam.simplejsonrpc.client.generator.CurrentTimeIdGenerator;
|
||||||
|
import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcMethod;
|
||||||
|
import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcParam;
|
||||||
|
import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcService;
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
|
import com.sparrowwallet.drongo.KeyPurpose;
|
||||||
|
import com.sparrowwallet.drongo.Utils;
|
||||||
|
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||||
|
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||||
|
import com.sparrowwallet.drongo.wallet.TransactionReference;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import javafx.concurrent.Service;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
import javax.net.ssl.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ElectrumServer {
|
||||||
|
private static Transport transport;
|
||||||
|
|
||||||
|
private synchronized Transport getTransport() throws ServerException {
|
||||||
|
if(transport == null) {
|
||||||
|
try {
|
||||||
|
String electrumServer = Config.get().getElectrumServer();
|
||||||
|
File electrumServerCert = Config.get().getElectrumServerCert();
|
||||||
|
|
||||||
|
if(electrumServer == null) {
|
||||||
|
throw new ServerException("Electrum server URL not specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(electrumServerCert != null && !electrumServerCert.exists()) {
|
||||||
|
throw new ServerException("Electrum server certificate file not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
Protocol protocol = Protocol.getProtocol(electrumServer);
|
||||||
|
if(protocol == null) {
|
||||||
|
throw new ServerException("Electrum server URL must start with " + Protocol.TCP.toUrlString() + " or " + Protocol.SSL.toUrlString());
|
||||||
|
}
|
||||||
|
|
||||||
|
HostAndPort server = protocol.getServerHostAndPort(electrumServer);
|
||||||
|
transport = protocol.getTransport(server, electrumServerCert);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServerException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transport;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServerVersion() throws ServerException {
|
||||||
|
JsonRpcClient client = new JsonRpcClient(getTransport());
|
||||||
|
List<String> serverVersion = client.createRequest().returnAsList(String.class).method("server.version").id(1).param("client_name", "Sparrow").param("protocol_version", "1.4").execute();
|
||||||
|
return serverVersion.get(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getHistory(Wallet wallet) throws ServerException {
|
||||||
|
getHistory(wallet, KeyPurpose.RECEIVE);
|
||||||
|
getHistory(wallet, KeyPurpose.CHANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getHistory(Wallet wallet, KeyPurpose keyPurpose) throws ServerException {
|
||||||
|
getHistory(wallet.getNode(keyPurpose).getChildren());
|
||||||
|
getMempool(wallet.getNode(keyPurpose).getChildren());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getHistory(Collection<Wallet.Node> nodes) throws ServerException {
|
||||||
|
getReferences("blockchain.scripthash.get_history", nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getMempool(Collection<Wallet.Node> nodes) throws ServerException {
|
||||||
|
getReferences("blockchain.scripthash.get_mempool", nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getReferences(String method, Collection<Wallet.Node> nodes) throws ServerException {
|
||||||
|
try {
|
||||||
|
JsonRpcClient client = new JsonRpcClient(getTransport());
|
||||||
|
BatchRequestBuilder<String, ScriptHashTx[]> batchRequest = client.createBatchRequest().keysType(String.class).returnType(ScriptHashTx[].class);
|
||||||
|
for(Wallet.Node node : nodes) {
|
||||||
|
batchRequest.add(node.getDerivationPath(), method, getScriptHash(node));
|
||||||
|
}
|
||||||
|
Map<String, ScriptHashTx[]> result = batchRequest.execute();
|
||||||
|
|
||||||
|
for(String path : result.keySet()) {
|
||||||
|
ScriptHashTx[] txes = result.get(path);
|
||||||
|
|
||||||
|
Optional<Wallet.Node> optionalNode = nodes.stream().filter(n -> n.getDerivationPath().equals(path)).findFirst();
|
||||||
|
if(optionalNode.isPresent()) {
|
||||||
|
Wallet.Node node = optionalNode.get();
|
||||||
|
Set<TransactionReference> references = Arrays.stream(txes).map(ScriptHashTx::getTransactionReference).collect(Collectors.toSet());
|
||||||
|
|
||||||
|
for(TransactionReference reference : references) {
|
||||||
|
if(!node.getHistory().add(reference)) {
|
||||||
|
Optional<TransactionReference> optionalReference = node.getHistory().stream().filter(tr -> tr.getTransactionId().equals(reference.getTransactionId())).findFirst();
|
||||||
|
if(optionalReference.isPresent()) {
|
||||||
|
TransactionReference existingReference = optionalReference.get();
|
||||||
|
if(existingReference.getHeight() < reference.getHeight()) {
|
||||||
|
node.getHistory().remove(existingReference);
|
||||||
|
node.getHistory().add(reference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
throw new ServerException(e.getCause());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServerException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getReferencedTransactions(Wallet wallet) throws ServerException {
|
||||||
|
getReferencedTransactions(wallet, KeyPurpose.RECEIVE);
|
||||||
|
getReferencedTransactions(wallet, KeyPurpose.CHANGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getReferencedTransactions(Wallet wallet, KeyPurpose keyPurpose) throws ServerException {
|
||||||
|
Wallet.Node purposeNode = wallet.getNode(keyPurpose);
|
||||||
|
Set<TransactionReference> references = new HashSet<>();
|
||||||
|
for(Wallet.Node addressNode : purposeNode.getChildren()) {
|
||||||
|
references.addAll(addressNode.getHistory());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Transaction> transactionMap = getTransactions(references);
|
||||||
|
wallet.getTransactions().putAll(transactionMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Transaction> getTransactions(Set<TransactionReference> references) throws ServerException {
|
||||||
|
try {
|
||||||
|
JsonRpcClient client = new JsonRpcClient(getTransport());
|
||||||
|
BatchRequestBuilder<String, String> batchRequest = client.createBatchRequest().keysType(String.class).returnType(String.class);
|
||||||
|
for(TransactionReference reference : references) {
|
||||||
|
batchRequest.add(reference.getTransactionId(), "blockchain.transaction.get", reference.getTransactionId());
|
||||||
|
}
|
||||||
|
Map<String, String> result = batchRequest.execute();
|
||||||
|
|
||||||
|
Map<String, Transaction> transactionMap = new HashMap<>();
|
||||||
|
for(String txid : result.keySet()) {
|
||||||
|
byte[] rawtx = Utils.hexToBytes(result.get(txid));
|
||||||
|
Transaction transaction = new Transaction(rawtx);
|
||||||
|
transactionMap.put(txid, transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactionMap;
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
throw new ServerException(e.getCause());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServerException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getHistory(Wallet.Node node) throws ServerException {
|
||||||
|
getHistory(getScriptHash(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getHistory(String scriptHash) throws ServerException {
|
||||||
|
try {
|
||||||
|
JsonRpcClient client = new JsonRpcClient(getTransport());
|
||||||
|
List<ScriptHashTx> txList = client.onDemand(ELectrumXService.class).getHistory(scriptHash);
|
||||||
|
for(ScriptHashTx tx : txList) {
|
||||||
|
System.out.println(tx);
|
||||||
|
}
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
throw new ServerException(e.getCause());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServerException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getScriptHash(Wallet.Node node) {
|
||||||
|
byte[] hash = Sha256Hash.hash(node.getOutputScript().getProgram());
|
||||||
|
byte[] reversed = Utils.reverseBytes(hash);
|
||||||
|
return Utils.bytesToHex(reversed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ScriptHashTx {
|
||||||
|
public int height;
|
||||||
|
public String tx_hash;
|
||||||
|
public long fee;
|
||||||
|
|
||||||
|
public TransactionReference getTransactionReference() {
|
||||||
|
return new TransactionReference(tx_hash, height, fee);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ScriptHashTx{" +
|
||||||
|
"height=" + height +
|
||||||
|
", tx_hash='" + tx_hash + '\'' +
|
||||||
|
", fee=" + fee +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonRpcService
|
||||||
|
@JsonRpcId(CurrentTimeIdGenerator.class)
|
||||||
|
@JsonRpcParams(ParamsType.MAP)
|
||||||
|
private interface ELectrumXService {
|
||||||
|
|
||||||
|
@JsonRpcMethod("blockchain.scripthash.get_history")
|
||||||
|
List<ScriptHashTx> getHistory(@JsonRpcParam("scripthash") String scriptHash);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TcpTransport implements Transport {
|
||||||
|
private static final int DEFAULT_PORT = 50001;
|
||||||
|
|
||||||
|
protected final HostAndPort server;
|
||||||
|
private final SocketFactory socketFactory;
|
||||||
|
|
||||||
|
private Socket socket;
|
||||||
|
|
||||||
|
public TcpTransport(HostAndPort server) {
|
||||||
|
this.server = server;
|
||||||
|
this.socketFactory = SocketFactory.getDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String pass(@NotNull String request) throws IOException {
|
||||||
|
if(socket == null) {
|
||||||
|
socket = createSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
writeRequest(socket, request);
|
||||||
|
} catch (IOException e) {
|
||||||
|
socket = createSocket();
|
||||||
|
writeRequest(socket, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
return readResponse(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeRequest(Socket socket, String request) throws IOException {
|
||||||
|
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
|
||||||
|
out.println(request);
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readResponse(Socket socket) throws IOException {
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||||
|
String response = in.readLine();
|
||||||
|
|
||||||
|
if(response == null) {
|
||||||
|
throw new IOException("Could not connect to server at " + Config.get().getElectrumServer());
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Socket createSocket() throws IOException {
|
||||||
|
return socketFactory.createSocket(server.getHost(), server.getPortOrDefault(DEFAULT_PORT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TcpOverTlsTransport extends TcpTransport {
|
||||||
|
private static final int DEFAULT_PORT = 50002;
|
||||||
|
|
||||||
|
private final SSLSocketFactory sslSocketFactory;
|
||||||
|
|
||||||
|
public TcpOverTlsTransport(HostAndPort server) throws NoSuchAlgorithmException, KeyManagementException {
|
||||||
|
super(server);
|
||||||
|
|
||||||
|
TrustManager[] trustAllCerts = new TrustManager[]{
|
||||||
|
new X509TrustManager() {
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return new X509Certificate[0];
|
||||||
|
}
|
||||||
|
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
|
||||||
|
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
|
sslContext.init(null, trustAllCerts, new SecureRandom());
|
||||||
|
|
||||||
|
this.sslSocketFactory = sslContext.getSocketFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TcpOverTlsTransport(HostAndPort server, File crtFile) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||||
|
super(server);
|
||||||
|
|
||||||
|
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));
|
||||||
|
|
||||||
|
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
|
keyStore.load(null, null);
|
||||||
|
keyStore.setCertificateEntry("electrumx", certificate);
|
||||||
|
|
||||||
|
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||||
|
trustManagerFactory.init(keyStore);
|
||||||
|
|
||||||
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
|
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
|
||||||
|
|
||||||
|
sslSocketFactory = sslContext.getSocketFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Socket createSocket() throws IOException {
|
||||||
|
SSLSocket sslSocket = (SSLSocket)sslSocketFactory.createSocket(server.getHost(), server.getPortOrDefault(DEFAULT_PORT));
|
||||||
|
sslSocket.startHandshake();
|
||||||
|
|
||||||
|
return sslSocket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TransactionHistoryService extends Service<Boolean> {
|
||||||
|
private final Wallet wallet;
|
||||||
|
|
||||||
|
public TransactionHistoryService(Wallet wallet) {
|
||||||
|
this.wallet = wallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Task<Boolean> createTask() {
|
||||||
|
return new Task<>() {
|
||||||
|
protected Boolean call() throws ServerException {
|
||||||
|
ElectrumServer electrumServer = new ElectrumServer();
|
||||||
|
electrumServer.getHistory(wallet);
|
||||||
|
electrumServer.getReferencedTransactions(wallet);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Protocol {
|
||||||
|
TCP {
|
||||||
|
@Override
|
||||||
|
public Transport getTransport(HostAndPort server, File serverCert) throws IOException {
|
||||||
|
return new TcpTransport(server);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SSL{
|
||||||
|
@Override
|
||||||
|
public Transport getTransport(HostAndPort server, File serverCert) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||||
|
if(serverCert != null && serverCert.exists()) {
|
||||||
|
return new TcpOverTlsTransport(server, serverCert);
|
||||||
|
} else {
|
||||||
|
return new TcpOverTlsTransport(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public abstract Transport getTransport(HostAndPort server, File serverCert) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException;
|
||||||
|
|
||||||
|
public HostAndPort getServerHostAndPort(String url) {
|
||||||
|
return HostAndPort.fromString(url.substring(this.toUrlString().length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toUrlString() {
|
||||||
|
return toString().toLowerCase() + "://";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Protocol getProtocol(String url) {
|
||||||
|
if(url.startsWith("tcp://")) {
|
||||||
|
return TCP;
|
||||||
|
}
|
||||||
|
if(url.startsWith("ssl://")) {
|
||||||
|
return SSL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
|
public class ServerException extends Exception {
|
||||||
|
public ServerException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
import com.sparrowwallet.drongo.SecureString;
|
import com.sparrowwallet.drongo.SecureString;
|
||||||
import com.sparrowwallet.drongo.Utils;
|
import com.sparrowwallet.drongo.Utils;
|
||||||
import com.sparrowwallet.drongo.crypto.*;
|
import com.sparrowwallet.drongo.crypto.*;
|
||||||
|
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
import com.sparrowwallet.drongo.wallet.MnemonicException;
|
import com.sparrowwallet.drongo.wallet.MnemonicException;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
@ -57,6 +58,8 @@ public class Storage {
|
||||||
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeyDeserializer());
|
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeyDeserializer());
|
||||||
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArraySerializer());
|
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArraySerializer());
|
||||||
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArrayDeserializer());
|
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArrayDeserializer());
|
||||||
|
gsonBuilder.registerTypeAdapter(Transaction.class, new TransactionSerializer());
|
||||||
|
gsonBuilder.registerTypeAdapter(Transaction.class, new TransactionDeserializer());
|
||||||
if(includeWalletSerializers) {
|
if(includeWalletSerializers) {
|
||||||
gsonBuilder.registerTypeAdapter(Keystore.class, new KeystoreSerializer());
|
gsonBuilder.registerTypeAdapter(Keystore.class, new KeystoreSerializer());
|
||||||
gsonBuilder.registerTypeAdapter(Wallet.Node.class, new NodeSerializer());
|
gsonBuilder.registerTypeAdapter(Wallet.Node.class, new NodeSerializer());
|
||||||
|
@ -267,6 +270,27 @@ public class Storage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class TransactionSerializer implements JsonSerializer<Transaction> {
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(Transaction src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
src.bitcoinSerializeToStream(baos);
|
||||||
|
return new JsonPrimitive(Utils.bytesToHex(baos.toByteArray()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TransactionDeserializer implements JsonDeserializer<Transaction> {
|
||||||
|
@Override
|
||||||
|
public Transaction deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
byte[] rawTx = Utils.hexToBytes(json.getAsJsonPrimitive().getAsString());
|
||||||
|
return new Transaction(rawTx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class KeystoreSerializer implements JsonSerializer<Keystore> {
|
private static class KeystoreSerializer implements JsonSerializer<Keystore> {
|
||||||
@Override
|
@Override
|
||||||
public JsonElement serialize(Keystore keystore, Type typeOfSrc, JsonSerializationContext context) {
|
public JsonElement serialize(Keystore keystore, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
@ -296,6 +320,10 @@ public class Storage {
|
||||||
if(childObject.get("children") != null && childObject.getAsJsonArray("children").size() == 0) {
|
if(childObject.get("children") != null && childObject.getAsJsonArray("children").size() == 0) {
|
||||||
childObject.remove("children");
|
childObject.remove("children");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(childObject.get("history") != null && childObject.getAsJsonArray("history").size() == 0) {
|
||||||
|
childObject.remove("history");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonObject;
|
return jsonObject;
|
||||||
|
@ -310,6 +338,9 @@ public class Storage {
|
||||||
if(childNode.getChildren() == null) {
|
if(childNode.getChildren() == null) {
|
||||||
childNode.setChildren(new TreeSet<>());
|
childNode.setChildren(new TreeSet<>());
|
||||||
}
|
}
|
||||||
|
if(childNode.getHistory() == null) {
|
||||||
|
childNode.setHistory(new TreeSet<>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
|
|
|
@ -12,5 +12,9 @@ open module com.sparrowwallet.sparrow {
|
||||||
requires com.google.gson;
|
requires com.google.gson;
|
||||||
requires com.google.zxing;
|
requires com.google.zxing;
|
||||||
requires com.google.zxing.javase;
|
requires com.google.zxing.javase;
|
||||||
|
requires simple.json.rpc.client;
|
||||||
|
requires simple.json.rpc.core;
|
||||||
|
requires org.jetbrains.annotations;
|
||||||
|
requires com.fasterxml.jackson.databind;
|
||||||
requires javafx.swing;
|
requires javafx.swing;
|
||||||
}
|
}
|
Loading…
Reference in a new issue