Skip to content

Commit

Permalink
Merge pull request #300 from yyajphd/develop
Browse files Browse the repository at this point in the history
Add node rewards
  • Loading branch information
LucasMLK authored Mar 6, 2024
2 parents 56290ee + d471895 commit 5c4d04f
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 47 deletions.
59 changes: 40 additions & 19 deletions src/main/java/io/xdag/cli/Commands.java
Original file line number Diff line number Diff line change
Expand Up @@ -301,13 +301,12 @@ private List<BlockWrapper> createTransactionBlock(Map<Address, KeyPair> ourKeys,
if (keys.size() != 0) {
res.add(createTransaction(to, amount, keys, remark));
}

return res;
}

private BlockWrapper createTransaction(Bytes32 to, XAmount amount, Map<Address, KeyPair> keys, String remark) {
List<Address> tos = Lists.newArrayList(new Address(to, XDAG_FIELD_OUTPUT, amount, true));
Block block = kernel.getBlockchain().createNewBlock(new HashMap<>(keys), tos, false, remark, XAmount.of(100,XUnit.MILLI_XDAG));
Block block = kernel.getBlockchain().createNewBlock(new HashMap<>(keys), tos, false, remark, XAmount.of(100, XUnit.MILLI_XDAG));

if (block == null) {
return null;
Expand Down Expand Up @@ -449,11 +448,11 @@ public String printBlockInfo(Block block, boolean raw) {
outputs.append(String.format(" output: %s %s%n",
output.getIsAddress() ? toBase58(hash2byte(output.getAddress())) : hash2Address(output.getAddress()),
getStateByFlags(block.getInfo().getFlags()).equals(MAIN.getDesc()) ? output.getAmount().toDecimal(9, XUnit.XDAG).toPlainString() :
block.getInputs().isEmpty() ? XAmount.ZERO.toDecimal(9,XUnit.XDAG).toPlainString() :
output.getAmount().subtract(MIN_GAS).toDecimal(9, XUnit.XDAG).toPlainString()
block.getInputs().isEmpty() ? XAmount.ZERO.toDecimal(9, XUnit.XDAG).toPlainString() :
output.getAmount().subtract(MIN_GAS).toDecimal(9, XUnit.XDAG).toPlainString()
));
//three type of block, 1、main block :getStateByFlags(block.getInfo().getFlags()).equals(MAIN.getDesc())
//2、link block:block.getInputs().isEmpty() 3、else transaction block
// three type of block, 1、main block :getStateByFlags(block.getInfo().getFlags()).equals(MAIN.getDesc())
// 2、link block:block.getInputs().isEmpty() 3、else transaction block
}
}
}
Expand All @@ -466,13 +465,13 @@ public String printBlockInfo(Block block, boolean raw) {
StringBuilder tx = new StringBuilder();
if (getStateByFlags(block.getInfo().getFlags()).equals(MAIN.getDesc()) && block.getInfo().getHeight() > kernel.getConfig().getSnapshotSpec().getSnapshotHeight()) {
tx.append(String.format(" earn: %s %s %s%n", hash2Address(block.getHashLow()),
kernel.getBlockchain().getReward(block.getInfo().getHeight()).toDecimal(9, XUnit.XDAG).toPlainString(),
FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS")
.format(XdagTime.xdagTimestampToMs(block.getTimestamp()))))
.append(String.format("fee earn: %s %s %s%n", hash2Address(block.getHashLow()),
kernel.getBlockStore().getBlockInfoByHash(block.getHashLow()).getFee().toDecimal(9, XUnit.XDAG).toPlainString(),
FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS")
.format(XdagTime.xdagTimestampToMs(block.getTimestamp()))));
kernel.getBlockchain().getReward(block.getInfo().getHeight()).toDecimal(9, XUnit.XDAG).toPlainString(),
FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS")
.format(XdagTime.xdagTimestampToMs(block.getTimestamp()))))
.append(String.format("fee earn: %s %s %s%n", hash2Address(block.getHashLow()),
kernel.getBlockStore().getBlockInfoByHash(block.getHashLow()).getFee().toDecimal(9, XUnit.XDAG).toPlainString(),
FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS")
.format(XdagTime.xdagTimestampToMs(block.getTimestamp()))));
}
for (TxHistory txHistory : kernel.getBlockchain().getBlockTxHistoryByAddress(block.getHashLow(), 1)) {
Address address = txHistory.getAddress();
Expand Down Expand Up @@ -500,8 +499,8 @@ public String printBlockInfo(Block block, boolean raw) {
}

// TODO need add block as transaction
//three type of block, main block :getStateByFlags(block.getInfo().getFlags()).equals(MAIN.getDesc())
//link block:block.getInputs().isEmpty() else transaction block
// three type of block, main block :getStateByFlags(block.getInfo().getFlags()).equals(MAIN.getDesc())
// link block:block.getInputs().isEmpty() else transaction block
return String.format(heightFormat, block.getInfo().getHeight()) + String.format(otherFormat,
FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSS").format(time),
Long.toHexString(block.getTimestamp()),
Expand All @@ -514,8 +513,8 @@ public String printBlockInfo(Block block, boolean raw) {
block.getInfo().getRef() == null ? "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" : hash2Address(Bytes32.wrap(block.getInfo().getRef())),
block.getInfo().getRef() == null ? XAmount.ZERO.toDecimal(9, XUnit.XDAG).toPlainString() :
(getStateByFlags(block.getInfo().getFlags()).equals(MAIN.getDesc()) ? kernel.getBlockStore().getBlockInfoByHash(block.getHashLow()).getFee().toDecimal(9, XUnit.XDAG).toPlainString() :
(block.getInputs().isEmpty() ? XAmount.ZERO.toDecimal(9,XUnit.XDAG).toPlainString() :
MIN_GAS.multiply(block.getOutputs().size()).toDecimal(9,XUnit.XDAG).toPlainString()))
(block.getInputs().isEmpty() ? XAmount.ZERO.toDecimal(9, XUnit.XDAG).toPlainString() :
MIN_GAS.multiply(block.getOutputs().size()).toDecimal(9, XUnit.XDAG).toPlainString()))
)
+ "\n"
+ (inputs == null ? "" : inputs.toString()) + (outputs == null ? "" : outputs.toString())
Expand Down Expand Up @@ -580,7 +579,7 @@ public String listConnect() {
return stringBuilder.toString();
}

public String pool(){
public String pool() {
return ChannelSupervise.showChannel();
}

Expand Down Expand Up @@ -679,7 +678,7 @@ public String xferToNew() {
Bytes32 accountHash = keyPair2Hash(kernel.getWallet().getDefKey());
to.set(8, accountHash.slice(8, 20));

String remark = "old balance to new address";
String remark = "block balance to new address";

// 转账输入
Map<Address, KeyPair> ourBlocks = Maps.newHashMap();
Expand Down Expand Up @@ -711,4 +710,26 @@ public String xferToNew() {
}
return str.append("}, it will take several minutes to complete the transaction.").toString();
}

// Distribute block rewards to node
public StringBuilder xferToNode(Map<Address, KeyPair> paymentsToNodesMap) {
StringBuilder str = new StringBuilder("Tx hash paid to the node :{");
MutableBytes32 to = MutableBytes32.create();
Bytes32 accountHash = keyPair2Hash(kernel.getWallet().getDefKey());
to.set(8, accountHash.slice(8, 20));
String remark = "Pay to " + kernel.getConfig().getNodeSpec().getNodeTag();
// Generate tx block to reward node
List<BlockWrapper> txs = createTransactionBlock(paymentsToNodesMap, to, remark);
for (BlockWrapper blockWrapper : txs) {
ImportResult result = kernel.getSyncMgr().validateAndAddNewBlock(blockWrapper);
if (result == ImportResult.IMPORTED_BEST || result == ImportResult.IMPORTED_NOT_BEST) {
kernel.getChannelMgr().sendNewBlock(blockWrapper);
str.append(BasicUtils.hash2Address(blockWrapper.getBlock().getHashLow()));
} else {
return new StringBuilder("This transaction block is invalid. Tx hash:")
.append(BasicUtils.hash2Address(blockWrapper.getBlock().getHashLow()));
}
}
return str.append("}");
}
}
2 changes: 2 additions & 0 deletions src/main/java/io/xdag/config/AbstractConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public class AbstractConfig implements Config, AdminSpec, NodeSpec, WalletSpec,
// =========================
protected String fundAddress;
protected double fundRation;
protected double nodeRation;
// =========================
// Network
// =========================
Expand Down Expand Up @@ -264,6 +265,7 @@ public void getSetting() {
txPageSizeLimit = config.hasPath("node.transaction.history.pageSizeLimit") ? config.getInt("node.transaction.history.pageSizeLimit") : 500;
fundAddress = config.hasPath("fund.address") ? config.getString("fund.address") : "4duPWMbYUgAifVYkKDCWxLvRRkSByf5gb";
fundRation = config.hasPath("fund.ration") ? config.getDouble("fund.ration") : 5;
nodeRation = config.hasPath("node.ration") ? config.getDouble("node.ration") : 5;
List<String> whiteIpList = config.getStringList("node.whiteIPs");
log.debug("{} IP access", whiteIpList.size());
for (String addr : whiteIpList) {
Expand Down
1 change: 1 addition & 0 deletions src/main/java/io/xdag/config/spec/NodeSpec.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,6 @@ public interface NodeSpec {
//reject transaction address;
String getRejectAddress();
boolean enableRefresh();
double getNodeRation();

}
11 changes: 6 additions & 5 deletions src/main/java/io/xdag/core/XdagField.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@

package io.xdag.core;

import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Map;
import lombok.Getter;
import lombok.Setter;
import org.apache.tuweni.bytes.MutableBytes;

import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Map;

public class XdagField implements Cloneable {

@Getter
Expand Down Expand Up @@ -92,8 +93,8 @@ public enum FieldType {
/***
*new tx type
*/
XDAG_FIELD_INPUT(0x0C),
XDAG_FIELD_OUTPUT(0x0D),
XDAG_FIELD_INPUT(0x0C),//12
XDAG_FIELD_OUTPUT(0x0D),//13
XDAG_FIELD_RESERVE5(0x0E),
XDAG_FIELD_RESERVE6(0x0F);

Expand Down
65 changes: 44 additions & 21 deletions src/main/java/io/xdag/pool/PoolAwardManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.xdag.Kernel;
import io.xdag.Wallet;
import io.xdag.cli.Commands;
import io.xdag.config.Config;
import io.xdag.core.*;
import io.xdag.net.websocket.ChannelSupervise;
Expand Down Expand Up @@ -30,19 +31,22 @@

@Slf4j
public class PoolAwardManagerImpl implements PoolAwardManager, Runnable {
private static final String TX_REMARK = "Block Reward Distribution";
private static final String TX_REMARK = "";
private final Kernel kernel;
protected Config config;
private final Blockchain blockchain;
private final Wallet wallet;
private final String fundAddress;
private final double fundRation;
private final double nodeRation;
private final Commands commands;
/**
* The hash of the past sixteen blocks
*/
protected List<Bytes32> blockPreHashs = new CopyOnWriteArrayList<>(new ArrayList<>(16));
protected List<Bytes32> blockHashs = new CopyOnWriteArrayList<>(new ArrayList<>(16));
protected List<Bytes32> minShares = new CopyOnWriteArrayList<>(new ArrayList<>(16));
private final Map<Address, KeyPair> paymentsToNodesMap = new HashMap<>(10);
private static final BlockingQueue<AwardBlock> awardBlockBlockingQueue = new LinkedBlockingQueue<>();

private final ExecutorService workExecutor = Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder()
Expand All @@ -54,10 +58,12 @@ public class PoolAwardManagerImpl implements PoolAwardManager, Runnable {
public PoolAwardManagerImpl(Kernel kernel) {
this.kernel = kernel;
this.config = kernel.getConfig();
this.wallet = kernel.getWallet();
this.fundAddress = config.getFundSpec().getFundAddress();
this.fundRation = Math.max(0, Math.min(config.getFundSpec().getFundRation(), 100));
this.nodeRation = Math.max(0, Math.min(config.getNodeSpec().getNodeRation(), 100));
this.blockchain = kernel.getBlockchain();
this.wallet = kernel.getWallet();
this.commands = new Commands(kernel);
init();
}

Expand Down Expand Up @@ -127,7 +133,7 @@ public int payPools(long time) {
log.info("Index of the block paid to the pool:{} ", paidBlockIndex);
int keyPos;

// Obtain the block hash and corresponding nocne to be paid
// Obtain the block hash and corresponding share to be paid
Bytes32 preHash = blockPreHashs.get(paidBlockIndex) == null ? null : blockPreHashs.get(paidBlockIndex);
Bytes32 hash = blockHashs.get(paidBlockIndex) == null ? null : blockHashs.get(paidBlockIndex);
Bytes32 share = minShares.get(paidBlockIndex) == null ? null : minShares.get(paidBlockIndex);
Expand All @@ -147,7 +153,6 @@ public int payPools(long time) {
log.debug("Can't find the block");
return -2;
}
//
if (compareTo(block.getNonce().slice(0, 20).toArray(), 0,
20, block.getCoinBase().getAddress().slice(8, 20).toArray(), 0, 20) == 0) {
log.debug("This block is not produced by mining and belongs to the node");
Expand All @@ -159,29 +164,45 @@ public int payPools(long time) {
keyPos = kernel.getBlockchain().getMemOurBlocks().get(hashlow);
}
if (keyPos < 0) {
log.debug("keyPos < 0,keyPos = {}", keyPos);
return -4;
}
XAmount payBalance = block.getInfo().getAmount();
if (compareAmountTo(payBalance, XAmount.ZERO) <= 0) {
XAmount allAmount = block.getInfo().getAmount();
if (compareAmountTo(allAmount, XAmount.ZERO) <= 0) {
log.debug("no main block,can't pay");
return -5;
}
Bytes32 poolWalletAddress = BasicUtils.hexPubAddress2Hashlow(String.valueOf(block.getNonce().slice(0, 20)));
XAmount sendAmount = block.getInfo().getAmount();
log.debug("=========== At this time {} starts to distribute rewards to pools===========", time);
TransactionInfoSender transactionInfoSender = new TransactionInfoSender();
transactionInfoSender.setPreHash(preHash);
transactionInfoSender.setShare(share);
doPayments(hashlow, sendAmount, poolWalletAddress, keyPos, transactionInfoSender);
doPayments(hashlow, allAmount, poolWalletAddress, keyPos, transactionInfoSender);
return 0;
}

public void doPayments(Bytes32 hashLow, XAmount sendAmount, Bytes32 poolWalletAddress, int keyPos,
public void doPayments(Bytes32 hashLow, XAmount allAmount, Bytes32 poolWalletAddress, int keyPos,
TransactionInfoSender transactionInfoSender) {
if (paymentsToNodesMap.size() == 10) {
StringBuilder txHash = commands.xferToNode(paymentsToNodesMap);
log.info(String.valueOf(txHash));
paymentsToNodesMap.clear();
}
// Foundation rewards, default reward ratio is 5%
XAmount fundAmount = sendAmount.multiply(div(fundRation, 100, 6));
XAmount fundAmount = allAmount.multiply(div(fundRation, 100, 6));
// Node rewards, default reward ratio is 5%
XAmount nodeAmount = allAmount.multiply(div(nodeRation, 100, 6));
// Pool rewards
XAmount poolAmount = sendAmount.subtract(fundAmount);
XAmount poolAmount = allAmount.subtract(fundAmount).subtract(nodeAmount);
// sendAmount = Foundation rewards + Pool rewards
XAmount sendAmount = allAmount.subtract(nodeAmount);
if (fundRation + nodeRation >= 100 || fundAmount.lessThan(MIN_GAS) || poolAmount.lessThan(MIN_GAS)) {
log.error("Block reward distribution failed.The fundRation and nodeRation parameter settings are " +
"unreasonable.Your fundRation:{} ," +
"nodeRation:{}", fundRation, nodeRation);
return;
}
// Amount output: community, pool and node
ArrayList<Address> receipt = new ArrayList<>(2);
if (sendAmount.compareTo(MIN_GAS.multiply(2)) >= 0) {
receipt.add(new Address(pubAddress2Hash(fundAddress), XDAG_FIELD_OUTPUT, fundAmount, true));
Expand All @@ -192,21 +213,25 @@ public void doPayments(Bytes32 hashLow, XAmount sendAmount, Bytes32 poolWalletAd
transactionInfoSender.setDonate(fundAmount.toDecimal(9, XUnit.XDAG).toPlainString());
log.debug("Start payment...");
transaction(hashLow, receipt, sendAmount, keyPos, transactionInfoSender);
paymentsToNodesMap.put(new Address(hashLow, XDAG_FIELD_IN, nodeAmount, false),
wallet.getAccount(keyPos));
log.info("The node's reward block was successfully placed,block hash:{},current Map size:{}",
hashLow.toHexString(), paymentsToNodesMap.size());
} else {
log.debug("The balance of block {} is insufficient and rewards will not be distributed. Maybe this block " +
"has been rollback",
hashLow.toHexString());
"has been rollback. send balance:{}",
hashLow.toHexString(), sendAmount.toDecimal(9, XUnit.XDAG).toPlainString());
}
receipt.clear();
}

public void transaction(Bytes32 hashLow, ArrayList<Address> receipt, XAmount sendAmount, int keypos,
public void transaction(Bytes32 hashLow, ArrayList<Address> receipt, XAmount sendAmount, int keyPos,
TransactionInfoSender transactionInfoSender) {
log.debug("All balance in this block: {}", sendAmount);
log.debug("unlock keypos =[{}]", keypos);
log.debug("Total balance pending transfer: {}", sendAmount);
log.debug("unlock keypos =[{}]", keyPos);
Map<Address, KeyPair> inputMap = new HashMap<>();
Address input = new Address(hashLow, XDAG_FIELD_IN, sendAmount, false);
KeyPair inputKey = wallet.getAccount(keypos);
KeyPair inputKey = wallet.getAccount(keyPos);
inputMap.put(input, inputKey);
Block block = blockchain.createNewBlock(inputMap, receipt, false, TX_REMARK, MIN_GAS);
if (inputKey.equals(wallet.getDefKey())) {
Expand All @@ -233,10 +258,8 @@ public void transaction(Bytes32 hashLow, ArrayList<Address> receipt, XAmount sen
} else {
log.error("Failed to add transaction history");
}
log.debug("The reward for block {} has been distributed to pool address {} ,send transaction " +
"information for pools to validate {}", hashLow, receipt.size() == 2 ?
WalletUtils.toBase58(receipt.get(0).getAddress().slice(8, 20).toArray()) :
" [Error: receipt error]", transactionInfoSender.toJsonString());
log.debug("The reward for block {} has been distributed to pool address {}", hashLow,
WalletUtils.toBase58(receipt.get(1).getAddress().slice(8, 20).toArray()));
}


Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/xdag-mainnet.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ admin.telnet.ip = 127.0.0.1
admin.telnet.port = 6001
admin.telnet.password = 123

# Pool websocket Config (Node <--> pools)
# Pool websocket Config (Node <--> Pools)
pool.whiteIPs = ["0.0.0.0"]
pool.ws.port = 7001

Expand All @@ -15,6 +15,7 @@ node.maxInboundConnectionsPerIp = 8
node.whiteIPs = ["127.0.0.1:8002"]
node.generate.block.enable = true
node.reject.transaction.address = 111111111111111111117K4nzc
node.ration = 5

# Fund config
fund.address = "4duPWMbYUgAifVYkKDCWxLvRRkSByf5gb"
Expand Down
2 changes: 2 additions & 0 deletions src/test/java/io/xdag/consensus/TaskTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.bytes.MutableBytes;
import org.hyperledger.besu.crypto.KeyPair;
import org.jline.utils.Log;
import org.json.JSONObject;
import org.junit.After;
import org.junit.Before;
Expand Down Expand Up @@ -174,6 +175,7 @@ public void testSend2PoolsTxInfoConvertToJsonFormat() {
transactionInfoSender.setFee(MIN_GAS.toDecimal(9, XDAG).toPlainString());
XAmount amount = XAmount.of(64, XDAG);
XAmount fundAmount = amount.multiply(div(fundRation, 100, 6));
Log.info(fundAmount.toDecimal(9,XDAG).toPlainString());
transactionInfoSender.setAmount(amount.subtract(MIN_GAS).subtract(fundAmount).toDecimal(9,
XDAG).toPlainString());
transactionInfoSender.setDonate(fundAmount.toDecimal(9, XDAG).toPlainString());
Expand Down
Loading

0 comments on commit 5c4d04f

Please sign in to comment.