Adding POTCOIN
This commit is contained in:
parent
7a90b625e4
commit
479120dcb8
|
@ -0,0 +1,35 @@
|
|||
package com.generalbytes.batm.server.extensions.extra.potcoin;
|
||||
|
||||
import com.generalbytes.batm.server.coinutil.AddressFormatException;
|
||||
import com.generalbytes.batm.server.coinutil.Base58;
|
||||
import com.generalbytes.batm.server.extensions.ExtensionsUtil;
|
||||
import com.generalbytes.batm.server.extensions.ICryptoAddressValidator;
|
||||
|
||||
public class PotcoinAddressValidator implements ICryptoAddressValidator {
|
||||
|
||||
@Override
|
||||
public boolean isAddressValid(String address) {
|
||||
if (address.startsWith("P")) {
|
||||
try {
|
||||
Base58.decodeToBigInteger(address);
|
||||
Base58.decodeChecked(address);
|
||||
} catch (AddressFormatException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPaperWalletSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mustBeBase58Address() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package com.generalbytes.batm.server.extensions.extra.potcoin;
|
||||
|
||||
import com.generalbytes.batm.server.extensions.*;
|
||||
import com.generalbytes.batm.server.extensions.extra.potcoin.sources.FixPriceRateSource;
|
||||
import com.generalbytes.batm.server.extensions.extra.potcoin.sources.coinmarketcap.CoinmarketcapRateSource;
|
||||
import com.generalbytes.batm.server.extensions.extra.potcoin.wallets.potwallet.Potwallet;
|
||||
import com.generalbytes.batm.server.extensions.watchlist.IWatchList;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
public class PotcoinExtension implements IExtension{
|
||||
@Override
|
||||
public String getName() {
|
||||
return "BATM Potcoin extension";
|
||||
}
|
||||
|
||||
@Override
|
||||
public IExchange createExchange(String exchangeLogin) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWallet createWallet(String walletLogin) {
|
||||
if (walletLogin !=null && !walletLogin.trim().isEmpty()) {
|
||||
StringTokenizer st = new StringTokenizer(walletLogin,":");
|
||||
String walletType = st.nextToken();
|
||||
|
||||
if ("potwallet".equalsIgnoreCase(walletType)) {
|
||||
String publicKey = st.nextToken();
|
||||
String privateKey = st.nextToken();
|
||||
String walletId = st.nextToken();
|
||||
|
||||
if (publicKey != null && privateKey != null && walletId != null) {
|
||||
return new Potwallet(publicKey,privateKey,walletId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICryptoAddressValidator createAddressValidator(String cryptoCurrency) {
|
||||
if (ICurrencies.POT.equalsIgnoreCase(cryptoCurrency)) {
|
||||
return new PotcoinAddressValidator();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPaperWalletGenerator createPaperWalletGenerator(String cryptoCurrency) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPaymentProcessor createPaymentProcessor(String paymentProcessorLogin) {
|
||||
return null; //no payment processors available
|
||||
}
|
||||
|
||||
@Override
|
||||
public IRateSource createRateSource(String sourceLogin) {
|
||||
if (sourceLogin != null && !sourceLogin.trim().isEmpty()) {
|
||||
StringTokenizer st = new StringTokenizer(sourceLogin,":");
|
||||
String rsType = st.nextToken();
|
||||
|
||||
if ("potfix".equalsIgnoreCase(rsType)) {
|
||||
BigDecimal rate = BigDecimal.ZERO;
|
||||
if (st.hasMoreTokens()) {
|
||||
try {
|
||||
rate = new BigDecimal(st.nextToken());
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
}
|
||||
String preferedFiatCurrency = ICurrencies.CAD;
|
||||
if (st.hasMoreTokens()) {
|
||||
preferedFiatCurrency = st.nextToken().toUpperCase();
|
||||
}
|
||||
return new FixPriceRateSource(rate,preferedFiatCurrency);
|
||||
} else if ("coinmarketcap".equalsIgnoreCase(rsType)) {
|
||||
String preferredFiatCurrency = ICurrencies.CAD;
|
||||
if (st.hasMoreTokens()) {
|
||||
preferredFiatCurrency = st.nextToken();
|
||||
}
|
||||
return new CoinmarketcapRateSource(preferredFiatCurrency);
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedCryptoCurrencies() {
|
||||
Set<String> result = new HashSet<String>();
|
||||
result.add(ICurrencies.POT);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedWatchListsNames() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IWatchList getWatchList(String name) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package com.generalbytes.batm.server.extensions.extra.potcoin.sources;
|
||||
|
||||
import com.generalbytes.batm.server.extensions.ICurrencies;
|
||||
import com.generalbytes.batm.server.extensions.IRateSource;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class FixPriceRateSource implements IRateSource {
|
||||
private BigDecimal rate = BigDecimal.ZERO;
|
||||
|
||||
private String preferedFiatCurrency = ICurrencies.CAD;
|
||||
|
||||
public FixPriceRateSource(BigDecimal rate,String preferedFiatCurrency) {
|
||||
this.rate = rate;
|
||||
if (ICurrencies.CAD.equalsIgnoreCase(preferedFiatCurrency)) {
|
||||
this.preferedFiatCurrency = ICurrencies.CAD;
|
||||
}
|
||||
if (ICurrencies.USD.equalsIgnoreCase(preferedFiatCurrency)) {
|
||||
this.preferedFiatCurrency = ICurrencies.USD;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getCryptoCurrencies() {
|
||||
Set<String> result = new HashSet<String>();
|
||||
result.add(ICurrencies.POT);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getExchangeRateLast(String cryptoCurrency, String fiatCurrency) {
|
||||
if (ICurrencies.POT.equalsIgnoreCase(cryptoCurrency)) {
|
||||
return rate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getFiatCurrencies() {
|
||||
Set<String> result = new HashSet<String>();
|
||||
result.add(ICurrencies.USD);
|
||||
result.add(ICurrencies.CAD);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferredFiatCurrency() {
|
||||
return preferedFiatCurrency;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package com.generalbytes.batm.server.extensions.extra.potcoin.sources.coinmarketcap;
|
||||
|
||||
import com.generalbytes.batm.server.extensions.ICurrencies;
|
||||
import com.generalbytes.batm.server.extensions.IRateSource;
|
||||
import si.mazi.rescu.RestProxyFactory;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class CoinmarketcapRateSource implements IRateSource {
|
||||
|
||||
private String preferedFiatCurrency = ICurrencies.USD;
|
||||
private ICoinmarketcapRateAPI api;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public CoinmarketcapRateSource(String preferedFiatCurrency) {
|
||||
if (ICurrencies.USD.equalsIgnoreCase(preferedFiatCurrency)) {
|
||||
this.preferedFiatCurrency = ICurrencies.USD;
|
||||
}
|
||||
if (ICurrencies.CAD.equalsIgnoreCase(preferedFiatCurrency)) {
|
||||
this.preferedFiatCurrency = ICurrencies.CAD;
|
||||
}
|
||||
if (ICurrencies.EUR.equalsIgnoreCase(preferedFiatCurrency)) {
|
||||
this.preferedFiatCurrency = ICurrencies.EUR;
|
||||
}
|
||||
api = RestProxyFactory.createProxy(ICoinmarketcapRateAPI.class, "https://api.coinmarketcap.com");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns list of supported crypto currencies
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getCryptoCurrencies() {
|
||||
Set<String> result = new HashSet<String>();
|
||||
result.add(ICurrencies.POT);
|
||||
//result.add(ICurrencies.BTC);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current price of cryptocurrency in specified fiat currency
|
||||
* @param cryptoCurrency
|
||||
* @param fiatCurrency
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public BigDecimal getExchangeRateLast(String cryptoCurrency, String fiatCurrency) {
|
||||
// coinmarketcap uses coin name instead coin symbol
|
||||
if (ICurrencies.BTC.equalsIgnoreCase(cryptoCurrency)) {
|
||||
cryptoCurrency = "bitcoin";
|
||||
} else if (ICurrencies.POT.equalsIgnoreCase(cryptoCurrency)) {
|
||||
cryptoCurrency = "potcoin";
|
||||
}
|
||||
|
||||
CoinmarketcapResponse[] response = api.getRequest(cryptoCurrency, fiatCurrency);
|
||||
|
||||
if (response != null) {
|
||||
if (ICurrencies.USD.equalsIgnoreCase(fiatCurrency)) {
|
||||
BigDecimal rate = response[0].getPrice_usd();
|
||||
return rate;
|
||||
} else if (ICurrencies.CAD.equalsIgnoreCase(fiatCurrency)) {
|
||||
BigDecimal rate = response[0].getPrice_cad();
|
||||
return rate;
|
||||
} else if (ICurrencies.EUR.equalsIgnoreCase(fiatCurrency)) {
|
||||
BigDecimal rate = response[0].getPrice_eur();
|
||||
return rate;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns list of supported fiat currencies
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getFiatCurrencies() {
|
||||
Set<String> result = new HashSet<String>();
|
||||
result.add(ICurrencies.USD);
|
||||
result.add(ICurrencies.CAD);
|
||||
result.add(ICurrencies.EUR);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns fiat currency that is used for actual purchases of cryptocurrency by server
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getPreferredFiatCurrency() {
|
||||
return preferedFiatCurrency;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.generalbytes.batm.server.extensions.extra.potcoin.sources.coinmarketcap;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class CoinmarketcapResponse {
|
||||
|
||||
private BigDecimal price_usd;
|
||||
private BigDecimal price_cad;
|
||||
private BigDecimal price_eur;
|
||||
|
||||
public BigDecimal getPrice_usd() {
|
||||
return price_usd;
|
||||
}
|
||||
|
||||
public void setPrice_usd(BigDecimal price_usd) {
|
||||
this.price_usd = price_usd;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice_cad() {
|
||||
return price_cad;
|
||||
}
|
||||
|
||||
public void setPrice_cad(BigDecimal price_cad) {
|
||||
this.price_cad = price_cad;
|
||||
}
|
||||
|
||||
public BigDecimal getPrice_eur() {
|
||||
return price_eur;
|
||||
}
|
||||
|
||||
public void setPrice_eur(BigDecimal price_eur) {
|
||||
this.price_eur = price_eur;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.generalbytes.batm.server.extensions.extra.potcoin.sources.coinmarketcap;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
@Path("/v1")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public interface ICoinmarketcapRateAPI {
|
||||
@GET
|
||||
@Path("/ticker/{cryptocurrency}/?convert={fiatcurrency}")
|
||||
CoinmarketcapResponse[] getRequest(@PathParam("cryptocurrency") String cryptoCurrency, @PathParam("fiatcurrency") String fiatCurrency);
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package com.generalbytes.batm.server.extensions.extra.potcoin.wallets.potwallet;
|
||||
|
||||
import com.generalbytes.batm.server.extensions.ICurrencies;
|
||||
import com.generalbytes.batm.server.extensions.IWallet;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import si.mazi.rescu.HttpStatusIOException;
|
||||
import si.mazi.rescu.RestProxyFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class Potwallet implements IWallet {
|
||||
private static final Logger log = LoggerFactory.getLogger("batm.master.Potwallet");
|
||||
|
||||
private String nonce;
|
||||
private String accessHash;
|
||||
|
||||
private String publicKey;
|
||||
private String privateKey;
|
||||
private String walletId;
|
||||
|
||||
private PotwalletAPI api;
|
||||
|
||||
public Potwallet(String publicKey, String privateKey, String walletId) {
|
||||
this.nonce = generateRandomString(8);
|
||||
|
||||
this.publicKey = publicKey;
|
||||
this.privateKey = privateKey;
|
||||
this.walletId = walletId;
|
||||
api = RestProxyFactory.createProxy(PotwalletAPI.class, "https://api.potwallet.com");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferredCryptoCurrency() {
|
||||
return ICurrencies.POT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getCryptoCurrencies() {
|
||||
Set<String> result = new HashSet<String>();
|
||||
result.add(ICurrencies.POT);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCryptoAddress(String cryptoCurrency) {
|
||||
if (!getCryptoCurrencies().contains(cryptoCurrency)) {
|
||||
log.error("Cryptocurrency " + cryptoCurrency + " not supported.");
|
||||
return null;
|
||||
}
|
||||
if (walletId != null) {
|
||||
return walletId;
|
||||
}
|
||||
try {
|
||||
accessHash = generateHash(privateKey, "https://api.potwallet.com/v1/address", nonce);
|
||||
PotwalletResponse response = api.getAddress(publicKey, accessHash, nonce);
|
||||
if (response != null && response.getMessage() != null && response.getSuccess()) {
|
||||
return new String(response.getMessage());
|
||||
}
|
||||
} catch (HttpStatusIOException e) {
|
||||
log.error(e.getHttpBody());
|
||||
} catch (IOException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getCryptoBalance(String cryptoCurrency) {
|
||||
if (!getCryptoCurrencies().contains(cryptoCurrency)) {
|
||||
log.error("Cryptocurrency " + cryptoCurrency + " not supported.");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
accessHash = generateHash(privateKey, "https://api.potwallet.com/v1/balance", nonce);
|
||||
PotwalletResponse response = api.getCryptoBalance(publicKey, accessHash, nonce);
|
||||
if (response != null && response.getMessage() != null && response.getSuccess()) {
|
||||
log.debug("Transaction " + response.getMessage() + " sent.");
|
||||
return new BigDecimal(response.getMessage());
|
||||
}
|
||||
} catch (HttpStatusIOException e) {
|
||||
log.error(e.getHttpBody());
|
||||
} catch (IOException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sendCoins(String destinationAddress, BigDecimal amount, String cryptoCurrency, String description) {
|
||||
if (!getCryptoCurrencies().contains(cryptoCurrency)) {
|
||||
log.error("Cryptocurrency " + cryptoCurrency + " not supported.");
|
||||
return null;
|
||||
}
|
||||
try{
|
||||
accessHash = generateHash(privateKey, "https://api.potwallet.com/v1/send", nonce);
|
||||
PotwalletResponse response = api.sendPots(publicKey, accessHash, nonce, destinationAddress, amount.stripTrailingZeros());
|
||||
if (response != null && response.getMessage() != null && response.getSuccess()) {
|
||||
return new String(response.getMessage());
|
||||
}
|
||||
} catch (HttpStatusIOException e) {
|
||||
log.error(e.getHttpBody());
|
||||
} catch (IOException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String generateRandomString(int length)
|
||||
{
|
||||
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
Random rng = new Random();
|
||||
char[] text = new char[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
text[i] = characters.charAt(rng.nextInt(characters.length()));
|
||||
}
|
||||
return new String(text);
|
||||
}
|
||||
|
||||
private static String generateHash(String privateHash, String endPoint, String nonce )
|
||||
{
|
||||
String digest = null;
|
||||
try
|
||||
{
|
||||
String content = endPoint + nonce;
|
||||
|
||||
SecretKeySpec key = new SecretKeySpec((privateHash).getBytes("UTF-8"), "HmacSHA256");
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(key);
|
||||
|
||||
byte[] bytes = mac.doFinal(content.getBytes("ASCII"));
|
||||
|
||||
StringBuffer hash = new StringBuffer();
|
||||
|
||||
for (int i=0; i<bytes.length; i++) {
|
||||
String hex = Integer.toHexString(0xFF & bytes[i]);
|
||||
if (hex.length() == 1) {
|
||||
hash.append('0');
|
||||
}
|
||||
hash.append(hex);
|
||||
}
|
||||
digest = hash.toString();
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch(InvalidKeyException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return digest ;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.generalbytes.batm.server.extensions.extra.potcoin.wallets.potwallet;
|
||||
|
||||
import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Path("/v1")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public interface PotwalletAPI {
|
||||
@GET
|
||||
@Path("/balance")
|
||||
PotwalletResponse getCryptoBalance(@HeaderParam("Access-Public") String publicKey, @HeaderParam("Access-Hash") String accessHash, @HeaderParam("Access-Nonce") String nonce) throws IOException;
|
||||
@GET
|
||||
@Path("/address")
|
||||
PotwalletResponse getAddress(@HeaderParam("Access-Public") String publicKey, @HeaderParam("Access-Hash") String accessHash, @HeaderParam("Access-Nonce") String nonce) throws IOException;
|
||||
@POST
|
||||
@Path("/send")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
PotwalletResponse sendPots(@HeaderParam("Access-Public") String publicKey, @HeaderParam("Access-Hash") String accessHash, @HeaderParam("Access-Nonce") String nonce, @PathParam("address") String address, @PathParam("amount") BigDecimal amount) throws IOException;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.generalbytes.batm.server.extensions.extra.potcoin.wallets.potwallet;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class PotwalletResponse {
|
||||
private Boolean success;
|
||||
private String message;
|
||||
|
||||
public Boolean getSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(Boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 7.1 KiB |
Loading…
Reference in New Issue