Explorar el Código

Withdraw and deposit update

master
Slixe hace 1 año
padre
commit
b4bb1a0f3d
Se han modificado 9 ficheros con 168 adiciones y 80 borrados
  1. +1
    -1
      src/main/java/fr/slixe/dero4j/Daemon.java
  2. +18
    -8
      src/main/java/fr/slixe/dero4j/DeroWallet.java
  3. +14
    -28
      src/main/java/fr/slixe/tipbot/ArangoDatabaseService.java
  4. +2
    -2
      src/main/java/fr/slixe/tipbot/TipBot.java
  5. +76
    -0
      src/main/java/fr/slixe/tipbot/Transaction.java
  6. +1
    -1
      src/main/java/fr/slixe/tipbot/command/InfoCommand.java
  7. +10
    -9
      src/main/java/fr/slixe/tipbot/command/WithdrawCommand.java
  8. +17
    -21
      src/main/java/fr/slixe/tipbot/task/VerifyTask.java
  9. +29
    -10
      src/main/java/fr/slixe/tipbot/task/WalletTask.java

+ 1
- 1
src/main/java/fr/slixe/dero4j/Daemon.java Ver fichero

@@ -15,7 +15,7 @@ public class Daemon
public Daemon(String host)
{
this.host = host + (host.endsWith("/") ? "" : "/") + "json_rpc";
this.host = (host.contains("://") ? "" : "http://") + host + (host.endsWith("/") ? "" : "/") + "json_rpc";
}
public Daemon(String host, int port)


+ 18
- 8
src/main/java/fr/slixe/dero4j/DeroWallet.java Ver fichero

@@ -11,10 +11,10 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
@@ -27,7 +27,7 @@ import fr.slixe.dero4j.util.MapBuilder;
public class DeroWallet implements IWallet
{
private static final Logger log = LoggerFactory.getLogger("Dero Wallet");
private static final Logger log = LoggerContext.getContext().getLogger("Dero Wallet");
private static final int SCALE = 12;
@@ -44,7 +44,7 @@ public class DeroWallet implements IWallet
private JSONObject request(JSONObject json) throws RequestException
{
System.out.println(json);
log.info(json.toString());
HttpResponse<JsonNode> req;
try {
req = Unirest.post(host).basicAuth(username, password).header("Content-Type", "application/json").body(json).asJson();
@@ -53,7 +53,7 @@ public class DeroWallet implements IWallet
throw new RequestException("Wallet is offline?");
}
JSONObject response = req.getBody().getObject();
System.out.println(response);
log.info(response.toString());
if (!response.has("result")) {
throw new RequestException(response.getJSONObject("error").getString("message"));
}
@@ -78,7 +78,11 @@ public class DeroWallet implements IWallet
@Override
public String transfer(String address, BigDecimal amount) throws RequestException
{
JSONObject json = request(json("transfer", new MapBuilder<String, Object>().put("address", address).put("amount", Helper.asUint64(amount, SCALE)).get()));
JSONObject dest = new JSONObject();
dest.put("address", address);
dest.put("amount", Helper.asUint64(amount, SCALE));
JSONObject json = request(json("transfer", new MapBuilder<String, Object>().put("destinations", new JSONArray().put(dest)).get()));
return json.getString("tx_hash");
}
@@ -168,13 +172,19 @@ public class DeroWallet implements IWallet
return null;
}
JSONObject result = json.getJSONObject("payments");
return new Tx.InPayment(result.getInt("block_height"), result.getString("tx_hash"), Helper.toBigDecimal(result.getBigInteger("amount"), SCALE), (byte) json.getInt("unlock_time"), json.getString("payment_id"));
System.out.println("PAYMENTS: " + result.toString());
return new Tx.InPayment(result.getInt("block_height"), result.getString("tx_hash"), Helper.toBigDecimal(result.getBigInteger("amount"), SCALE), (byte) result.getInt("unlock_time"), result.getString("payment_id"));
}
@Override
public BigDecimal estimateFee(String address, BigDecimal amount) throws RequestException
{
JSONObject json = request(json("transfer", new MapBuilder<String, Object>().put("do_not_relay", true).put("address", address).put("amount", Helper.asUint64(amount, SCALE)).get()));
JSONObject dest = new JSONObject();
dest.put("address", address);
dest.put("amount", Helper.asUint64(amount, SCALE));
JSONObject json = request(json("transfer", new MapBuilder<String, Object>().put("do_not_relay", true).put("destinations", new JSONArray().put(dest)).get()));
return Helper.toBigDecimal(json.getBigInteger("fee"), SCALE);
}
}

+ 14
- 28
src/main/java/fr/slixe/tipbot/ArangoDatabaseService.java Ver fichero

@@ -15,11 +15,9 @@ import com.arangodb.ArangoCollection;
import com.arangodb.ArangoCursor;
import com.arangodb.ArangoDB;
import com.arangodb.ArangoDatabase;
import com.arangodb.entity.BaseDocument;
import com.google.inject.Inject;
import fr.slixe.dero4j.RequestException;
import fr.slixe.dero4j.structure.Tx;
import fr.slixe.dero4j.util.MapBuilder;
@Singleton
@@ -187,49 +185,32 @@ public class ArangoDatabaseService
return all("FOR doc IN users RETURN doc", User.class, new HashMap<>());
}
public void addTx(Tx tx, String userId)
public void addTx(Transaction tx)
{
BaseDocument base = new BaseDocument();
base.addAttribute("amount", tx.getAmount());
base.addAttribute("userId", userId);
base.addAttribute("blockHeight", tx.getBlockHeight());
base.addAttribute("confirmed", false);
base.addAttribute("confirmations", 0);
base.setKey(tx.getTxHash());
txs.insertDocument(base);
txs.insertDocument(tx);
}
public void updateTx(String txHash, int confirmations)
{
BaseDocument doc = txs.getDocument(txHash, BaseDocument.class);
doc.updateAttribute("confirmations", confirmations);
Transaction doc = txs.getDocument(txHash, Transaction.class);
if (confirmations == 20)
doc.updateAttribute("confirmed", true);
doc.setConfirmations(confirmations);
txs.updateDocument(doc.getKey(), doc);
txs.updateDocument(doc.getHash(), doc);
}
public void removeTx(String txHash) {
txs.deleteDocument(txHash);
}
public List<BaseDocument> getConfirmedTxs()
public List<Transaction> getConfirmedTxs()
{
return getTxs(true);
return all("FOR doc IN txs FILTER doc.confirmations == @confirmations RETURN doc", Transaction.class, new MapBuilder<String, Object>().put("confirmations", 20).get());
}
public List<BaseDocument> getUnconfirmedTxs()
public List<Transaction> getUnconfirmedTxs()
{
return getTxs(false);
}
private List<BaseDocument> getTxs(boolean confirmed)
{
return all("FOR doc IN txs FILTER doc.confirmed == @confirmed RETURN doc", BaseDocument.class, new MapBuilder<String, Object>().put("confirmed", confirmed).get());
return all("FOR doc IN txs FILTER doc.confirmations < 20 RETURN doc", Transaction.class, new MapBuilder<String, Object>().get());
}
protected <T> T first(String query, Class<T> type, Map<String, Object> vars)
@@ -259,4 +240,9 @@ public class ArangoDatabaseService
return new User(userId, null, paymentId, BigDecimal.ZERO, BigDecimal.ZERO);
}
public boolean existTx(String txHash)
{
return txs.documentExists(txHash);
}
}

+ 2
- 2
src/main/java/fr/slixe/tipbot/TipBot.java Ver fichero

@@ -129,8 +129,8 @@ public class TipBot extends KrobotModule {
this.daemon = new Daemon(daemonHost);
timer.scheduleAtFixedRate(task, 0, TimeUnit.SECONDS.toMillis(30));
timer.scheduleAtFixedRate(verifyTask, 0, TimeUnit.SECONDS.toMillis(30));
timer.scheduleAtFixedRate(task, TimeUnit.SECONDS.toMillis(20), TimeUnit.SECONDS.toMillis(30));
timer.scheduleAtFixedRate(verifyTask, TimeUnit.SECONDS.toMillis(20), TimeUnit.SECONDS.toMillis(30));
}
public void loadConfig()


+ 76
- 0
src/main/java/fr/slixe/tipbot/Transaction.java Ver fichero

@@ -0,0 +1,76 @@
package fr.slixe.tipbot;

import java.math.BigDecimal;

import com.arangodb.entity.DocumentField;

public class Transaction {
@DocumentField(DocumentField.Type.KEY)
private String hash;
private String userId;
private long blockHeight;
private BigDecimal amount;
private int confirmations;
public Transaction() {}
public Transaction(String hash, String userId, long blockHeight, BigDecimal amount)
{
this.hash = hash;
this.userId = userId;
this.blockHeight = blockHeight;
this.amount = amount;
this.confirmations = 0;
}

public String getHash()
{
return hash;
}

public void setHash(String hash)
{
this.hash = hash;
}

public String getUserId()
{
return userId;
}

public void setUserId(String userId)
{
this.userId = userId;
}

public long getBlockHeight()
{
return blockHeight;
}

public void setBlockHeight(long blockHeight)
{
this.blockHeight = blockHeight;
}

public BigDecimal getAmount()
{
return amount;
}

public void setAmount(BigDecimal amount)
{
this.amount = amount;
}
public int getConfirmations()
{
return confirmations;
}

public void setConfirmations(int confirmations)
{
this.confirmations = confirmations;
}
}

+ 1
- 1
src/main/java/fr/slixe/tipbot/command/InfoCommand.java Ver fichero

@@ -69,7 +69,7 @@ public class InfoCommand implements CommandHandler
StringBuilder builder = new StringBuilder();
builder.append("Height / Topoheight: ").append(height + " / " + topoHeight).append("\n");
builder.append("Average Block Time: ").append(blockTime).append("\n");
builder.append("Average Block Time: ").append(blockTime).append("s").append("\n");
builder.append("Difficulty: ").append(difficulty).append("\n");
builder.append("Mempool: ").append(txMempool).append("\n");
builder.append("Total Supply: ").append(totalSupply).append("\n");


+ 10
- 9
src/main/java/fr/slixe/tipbot/command/WithdrawCommand.java Ver fichero

@@ -28,14 +28,14 @@ public class WithdrawCommand implements CommandHandler {
@Override
public Object handle(MessageContext ctx, ArgumentMap args) throws Exception { //TODO add Withdraw to a task and not call withdraw directly..
BigDecimal amount;
MessageChannel chan = ctx.getChannel();
if (!(chan instanceof PrivateChannel))
{
chan = ctx.getUser().openPrivateChannel().complete();
}
try {
amount = new BigDecimal(args.get("amount", String.class));
if (amount.signum() != 1)
@@ -45,15 +45,15 @@ public class WithdrawCommand implements CommandHandler {
{
throw new CommandException(String.format(bot.getMessage("withdraw.err.invalid-amount"), ctx.getUser().getAsMention()));
}
String address = args.get("address");
String id = ctx.getUser().getId();
if (!wallet.hasEnoughFunds(id, amount))
{
throw new CommandException(bot.getMessage("withdraw.err.not-enough"));
}
BigDecimal fee;
try {
fee = wallet.getApi().estimateFee(address, amount);
@@ -62,6 +62,7 @@ public class WithdrawCommand implements CommandHandler {
e.printStackTrace();
throw new CommandException(e.getMessage());
}
String tx;
try {
tx = wallet.getApi().transfer(address, amount);
@@ -69,11 +70,11 @@ public class WithdrawCommand implements CommandHandler {
e.printStackTrace();
throw new CommandException(bot.getMessage("withdraw.err.transfer"));
}
wallet.removeFunds(id, amount);
chan.sendMessage(bot.dialog("Withdraw", String.format("You've withdrawn %s **DERO** to:\n%s\n\n__**Tx hash**__:\n%s\n\n__**Fee**__: %s", amount.subtract(fee), address, tx, fee))).queue();
return null;
}
}

+ 17
- 21
src/main/java/fr/slixe/tipbot/task/VerifyTask.java Ver fichero

@@ -1,21 +1,20 @@
package fr.slixe.tipbot.task;
import java.math.BigDecimal;
import java.util.List;
import java.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import com.arangodb.entity.BaseDocument;
import com.google.inject.Inject;
import fr.slixe.dero4j.RequestException;
import fr.slixe.tipbot.Transaction;
import fr.slixe.tipbot.Wallet;
public class VerifyTask extends TimerTask {
private static final Logger log = LoggerFactory.getLogger("VerifyTask");
private static final Logger log = LoggerContext.getContext().getLogger("VerifyTask");
@Inject
private Wallet wallet;
@@ -32,30 +31,27 @@ public class VerifyTask extends TimerTask {
log.error("Looks like wallet isn't reachable...");
return;
}
List<BaseDocument> docs = wallet.getDB().getUnconfirmedTxs();
for (BaseDocument doc : docs)
List<Transaction> txs = wallet.getDB().getUnconfirmedTxs();
for (Transaction tx : txs)
{
String txHash = doc.getKey();
String userId = (String) doc.getAttribute("userId");
int txBlockHeight = (int) doc.getAttribute("blockHeight");
BigDecimal amount = (BigDecimal) doc.getAttribute("amount");
int diff = blockHeight - txBlockHeight;
int diff = (int) (blockHeight - tx.getBlockHeight());
diff = diff > 20 ? 20 : diff;
try {
if (diff == 20) //we wait 20 blocks to verify instead of veryfing every block
{
if (!this.wallet.getApi().isValidTx(txHash))
if (!this.wallet.getApi().isValidTx(tx.getHash()))
{
this.wallet.getDB().removeTx(txHash);
log.error("Invalid transaction !! hash: '" + tx.getHash() + "'");
this.wallet.getDB().removeTx(tx.getHash());
continue;
}
else {
this.wallet.addFunds(userId, amount);
this.wallet.removeUnconfirmedFunds(userId, amount);
log.info("Amount: " + tx.getAmount());
this.wallet.addFunds(tx.getUserId(), tx.getAmount());
this.wallet.removeUnconfirmedFunds(tx.getUserId(), tx.getAmount());
}
}
} catch (RequestException e) {
@@ -63,7 +59,7 @@ public class VerifyTask extends TimerTask {
continue;
}
this.wallet.getDB().updateTx(txHash, diff);
this.wallet.getDB().updateTx(tx.getHash(), diff);
}
}
}

+ 29
- 10
src/main/java/fr/slixe/tipbot/task/WalletTask.java Ver fichero

@@ -6,12 +6,13 @@ import java.util.TimerTask;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.krobot.Krobot;
import org.krobot.util.Dialog;
import com.google.inject.Inject;
import fr.slixe.dero4j.RequestException;
import fr.slixe.dero4j.structure.Tx;
import fr.slixe.tipbot.TipBot;
import fr.slixe.tipbot.Transaction;
import fr.slixe.tipbot.User;
import fr.slixe.tipbot.Wallet;
@@ -22,6 +23,9 @@ public class WalletTask extends TimerTask
@Inject
private Wallet wallet;
@Inject
private TipBot bot;
private int lastBlockHeight = 0;
public WalletTask() {}
@@ -45,37 +49,52 @@ public class WalletTask extends TimerTask
{
log.info("skip this execution, block height is the same.");
return;
} else if ((diff = blockHeight - this.lastBlockHeight) > 1)
} else if (blockHeight - this.lastBlockHeight > 1)
{
log.warn("Multiple blocks detected, current block height is " + blockHeight);
diff = blockHeight - this.lastBlockHeight;
log.warn("Multiple blocks detected, current wallet block height is " + blockHeight + " and last block height is " + this.lastBlockHeight);
blockHeight = blockHeight - diff + 1;
log.warn("Now, it's " + blockHeight + " with a diff at " + diff);
log.warn("Now, it's " + blockHeight);
}
if (blockHeight < 0)
blockHeight = 0;
final List<User> users = this.wallet.getDB().getUsers();
for (User doc : users) {
final String userId = doc.getKey(); //using userId as key
final String paymentId = (String) doc.getPaymentId();
if (paymentId == null) continue;
final List<Tx> transactions;
try {
log.info("Fetch bulk payments with blockHeight " + blockHeight);
transactions = this.wallet.getApi().getTransactions(paymentId, blockHeight);
} catch (RequestException e) {
e.printStackTrace();
continue;
}
Krobot.getRuntime().jda().getUserById(userId).openPrivateChannel().queue((e) -> {
log.info("Private channel opened with " + e.getUser().getName());
for (Tx tx : transactions) {
log.info("New incoming transaction for user " + userId);
if (this.wallet.getDB().existTx(tx.getTxHash()))
{
log.error(String.format("Duplicated TX Hash: '%s', ignored...", tx.getTxHash()));
continue;
}
this.wallet.addUnconfirmedFunds(userId, tx.getAmount());
this.wallet.getDB().addTx(tx, userId);
this.wallet.getDB().addTx(new Transaction(tx.getTxHash(), userId, tx.getBlockHeight(), tx.getAmount()));
e.sendMessage(Dialog.info("Deposit", String.format("__**Amount**__: %s\n__**Tx hash**__: %s\n__**Block height**__: %s", tx.getAmount(), tx.getTxHash(), tx.getBlockHeight()))).queue();
e.sendMessage(bot.dialog("Deposit", String.format("__**Amount**__: %s\n__**Tx hash**__: %s\n__**Block height**__: %s", tx.getAmount(), tx.getTxHash(), tx.getBlockHeight()))).queue();
}
});
}


Cargando…
Cancelar
Guardar