Skip to content

Commit

Permalink
add DOJO pairing
Browse files Browse the repository at this point in the history
  • Loading branch information
zeroleak committed Aug 8, 2019
1 parent db9afe0 commit 898e3d3
Show file tree
Hide file tree
Showing 7 changed files with 338 additions and 29 deletions.
97 changes: 81 additions & 16 deletions java/com/samourai/wallet/api/backend/BackendApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import com.samourai.wallet.api.backend.beans.HttpException;
import com.samourai.wallet.api.backend.beans.MultiAddrResponse;
import com.samourai.wallet.api.backend.beans.RefreshTokenResponse;
import com.samourai.wallet.api.backend.beans.UnspentResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -11,26 +13,31 @@
public class BackendApi {
private Logger log = LoggerFactory.getLogger(BackendApi.class);

private static final String URL_UNSPENT = "/v2/unspent?active=";
private static final String URL_MULTIADDR = "/v2/multiaddr?active=";
private static final String URL_INIT_BIP84 = "/v2/xpub";
private static final String URL_FEES = "/v2/fees";
private static final String URL_PUSHTX = "/v2/pushtx/";
private static final String URL_UNSPENT = "/unspent?active=";
private static final String URL_MULTIADDR = "/multiaddr?active=";
private static final String URL_INIT_BIP84 = "/xpub";
private static final String URL_FEES = "/fees";
private static final String URL_PUSHTX = "/pushtx/";
private static final String URL_GET_AUTH_LOGIN = "/auth/login";
private static final String URL_GET_AUTH_REFRESH = "/auth/refresh";

private IBackendClient httpClient;
private String urlBackend;
private String apiKey;

public BackendApi(IBackendClient httpClient, String urlBackend) {
public BackendApi(IBackendClient httpClient, String urlBackend, String apiKey) {
this.httpClient = httpClient;
this.urlBackend = urlBackend;
this.apiKey = apiKey; // may be null
}

public List<UnspentResponse.UnspentOutput> fetchUtxos(String zpub) throws Exception {
String url = urlBackend + URL_UNSPENT + zpub;
String url = computeAuthUrl(urlBackend + URL_UNSPENT + zpub);
if (log.isDebugEnabled()) {
log.debug("fetchUtxos: " + url);
}
UnspentResponse unspentResponse = httpClient.getJson(url, UnspentResponse.class);
Map<String,String> headers = computeHeaders();
UnspentResponse unspentResponse = httpClient.getJson(url, UnspentResponse.class, headers);
List<UnspentResponse.UnspentOutput> unspentOutputs =
new ArrayList<UnspentResponse.UnspentOutput>();
if (unspentResponse.unspent_outputs != null) {
Expand All @@ -40,11 +47,12 @@ public List<UnspentResponse.UnspentOutput> fetchUtxos(String zpub) throws Except
}

public List<MultiAddrResponse.Address> fetchAddresses(String zpub) throws Exception {
String url = urlBackend + URL_MULTIADDR + zpub;
String url = computeAuthUrl(urlBackend + URL_MULTIADDR + zpub);
if (log.isDebugEnabled()) {
log.debug("fetchAddress: " + url);
}
MultiAddrResponse multiAddrResponse = httpClient.getJson(url, MultiAddrResponse.class);
Map<String,String> headers = computeHeaders();
MultiAddrResponse multiAddrResponse = httpClient.getJson(url, MultiAddrResponse.class, headers);
List<MultiAddrResponse.Address> addresses = new ArrayList<MultiAddrResponse.Address>();
if (multiAddrResponse.addresses != null) {
addresses = Arrays.asList(multiAddrResponse.addresses);
Expand Down Expand Up @@ -72,20 +80,22 @@ public MultiAddrResponse.Address fetchAddress(String zpub) throws Exception {
}

public void initBip84(String zpub) throws Exception {
String url = urlBackend + URL_INIT_BIP84;
String url = computeAuthUrl(urlBackend + URL_INIT_BIP84);
if (log.isDebugEnabled()) {
log.debug("initBip84: zpub=" + zpub);
}
Map<String,String> headers = computeHeaders();
Map<String, String> postBody = new HashMap<String, String>();
postBody.put("xpub", zpub);
postBody.put("type", "new");
postBody.put("segwit", "bip84");
httpClient.postUrlEncoded(url, Void.class, postBody);
httpClient.postUrlEncoded(url, Void.class, headers, postBody);
}

public SamouraiFee fetchFees() throws Exception {
String url = urlBackend + URL_FEES;
Map<String, Integer> feeResponse = httpClient.getJson(url, Map.class);
String url = computeAuthUrl(urlBackend + URL_FEES);
Map<String,String> headers = computeHeaders();
Map<String, Integer> feeResponse = httpClient.getJson(url, Map.class, headers);
if (feeResponse == null) {
throw new Exception("Invalid fee response from server");
}
Expand All @@ -98,11 +108,12 @@ public void pushTx(String txHex) throws Exception {
} else {
log.info("pushTx...");
}
String url = urlBackend + URL_PUSHTX;
String url = computeAuthUrl(urlBackend + URL_PUSHTX);
Map<String,String> headers = computeHeaders();
Map<String, String> postBody = new HashMap<String, String>();
postBody.put("tx", txHex);
try {
httpClient.postUrlEncoded(url, Void.class, postBody);
httpClient.postUrlEncoded(url, Void.class, headers, postBody);
} catch (HttpException e) {
if (log.isDebugEnabled()) {
log.error("pushTx failed", e);
Expand All @@ -118,4 +129,58 @@ public void pushTx(String txHex) throws Exception {
"PushTx failed (" + e.getResponseBody() + ") for txHex=" + txHex);
}
}

protected RefreshTokenResponse.Authorization tokenAuthenticate() throws Exception {
String url = getUrlBackend() + URL_GET_AUTH_LOGIN;
if (log.isDebugEnabled()) {
log.debug("tokenAuthenticate");
}
Map<String, String> postBody = new HashMap<String, String>();
postBody.put("apikey", getApiKey());
RefreshTokenResponse response =
getHttpClient().postUrlEncoded(url, RefreshTokenResponse.class, null, postBody);

if (response.authorizations == null|| StringUtils.isEmpty(response.authorizations.access_token)) {
throw new Exception("Authorization refused. Invalid apiKey?");
}
return response.authorizations;
}

protected String tokenRefresh(String refreshToken) throws Exception {
String url = getUrlBackend() + URL_GET_AUTH_REFRESH;
if (log.isDebugEnabled()) {
log.debug("tokenRefresh");
}
Map<String, String> postBody = new HashMap<String, String>();
postBody.put("rt", refreshToken);
RefreshTokenResponse response =
getHttpClient().postUrlEncoded(url, RefreshTokenResponse.class, null, postBody);

if (response.authorizations == null || StringUtils.isEmpty(response.authorizations.access_token)) {
throw new Exception("Authorization refused. Invalid apiKey?");
}
return response.authorizations.access_token;
}

protected Map<String,String> computeHeaders() throws Exception {
Map<String,String> headers = new HashMap<String, String>();
return headers;
}

protected String computeAuthUrl(String url) throws Exception {
// override for auth support
return url;
}

protected IBackendClient getHttpClient() {
return httpClient;
}

protected String getApiKey() {
return apiKey;
}

public String getUrlBackend() {
return urlBackend;
}
}
4 changes: 2 additions & 2 deletions java/com/samourai/wallet/api/backend/IBackendClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import java.util.Map;

public interface IBackendClient {
<T> T getJson(String url, Class<T> responseType) throws HttpException;
<T> T getJson(String url, Class<T> responseType, Map<String,String> headers) throws HttpException;

<T> T postUrlEncoded(String url, Class<T> responseType, Map<String, String> body) throws HttpException;
<T> T postUrlEncoded(String url, Class<T> responseType, Map<String,String> headers, Map<String, String> body) throws HttpException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.samourai.wallet.api.backend.beans;

public class RefreshTokenResponse {
public Authorization authorizations;

public RefreshTokenResponse() {}

public static class Authorization {
public String access_token;
public String refresh_token;

public Authorization(){}
}
}
82 changes: 72 additions & 10 deletions java/com/samourai/wallet/api/pairing/PairingPayload.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,62 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URL;

public class PairingPayload {
private static final Logger log = LoggerFactory.getLogger(PairingPayload.class);

private CliPairingValue pairing;
private PairingValue pairing;
private PairingDojo dojo; // may be null

public PairingPayload() {
this.pairing = new CliPairingValue();
this.pairing = new PairingValue();
this.dojo = null;
}

public PairingPayload(PairingType type, PairingVersion version, PairingNetwork network, String mnemonic, Boolean passphrase) {
this.pairing = new CliPairingValue(type, version, network, mnemonic, passphrase);
public PairingPayload(PairingType type, PairingVersion version, PairingNetwork network, String mnemonic, Boolean passphrase, PairingDojo dojo) {
this.pairing = new PairingValue(type, version, network, mnemonic, passphrase);
this.dojo = dojo;
}

protected void validate() throws Exception {
if (pairing == null) {
throw new Exception("Invalid pairing");
}
pairing.validate();
if (dojo != null) {
dojo.validate();
}
}

public CliPairingValue getPairing() {
public PairingValue getPairing() {
return pairing;
}

public void setPairing(CliPairingValue pairing) {
public void setPairing(PairingValue pairing) {
this.pairing = pairing;
}

public static class CliPairingValue {
public PairingDojo getDojo() {
return dojo;
}

public void setDojo(PairingDojo dojo) {
this.dojo = dojo;
}

public static class PairingValue {
private PairingType type;
private PairingVersion version;
private PairingNetwork network;
private String mnemonic;
private Boolean passphrase; // NULL for V1

public CliPairingValue() {
public PairingValue() {

}
}

public CliPairingValue(PairingType type, PairingVersion version, PairingNetwork network, String mnemonic, Boolean passphrase) {
public PairingValue(PairingType type, PairingVersion version, PairingNetwork network, String mnemonic, Boolean passphrase) {
this.type = type;
this.version = version;
this.network = network;
Expand Down Expand Up @@ -110,4 +126,50 @@ public void setPassphrase(Boolean passphrase) {
}
}

public static class PairingDojo {
private String url; // may be null
private String apikey; // may be null

public PairingDojo() {
}

public PairingDojo(String url, String apikey) {
this.url = url;
this.apikey = apikey;
}

protected void validate() throws Exception {
// url
if (StringUtils.isEmpty(url)) {
throw new Exception("Invalid pairing.url");
}
try {
new URL(url);
} catch(Exception e) {
log.error("", e);
throw new Exception("Invalid pairing.url");
}

// apikey
if (StringUtils.isEmpty(apikey)) {
throw new Exception("Invalid pairing.apikey");
}
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public String getApikey() {
return apikey;
}

public void setApikey(String apikey) {
this.apikey = apikey;
}
}
}
3 changes: 2 additions & 1 deletion java/com/samourai/wallet/api/pairing/PairingVersion.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

public enum PairingVersion {
V1_0_0("1.0.0"),
V2_0_0("2.0.0");
V2_0_0("2.0.0"),
V3_0_0("3.0.0");

private String value;

Expand Down
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>java</sourceDirectory>
Expand Down
Loading

0 comments on commit 898e3d3

Please sign in to comment.