forked from Qortal/qortal
Fix issues with payments, asset trades and voting.
Some payments don't always initialize the recipient's last reference, depending on whether the asset is QORA or not and what type of transaction is being processed. Calls to Payment.process/orphan now take boolean to indicate whether recipient's last reference should always be initialized regardless of asset. ("initialized" here means setting an initial last reference for an account if the current value is null/empty/missing) When matching orders to produce trades, it isn't enough to only sort orders with best price first. Orders with the same price also need to be further sorted by earliest order first. (This was implicitly done in Qora v1). Added additional ORDER BY sub-clause to achieve this and improved the corresponding table index too. Converted a lot of logging from simplistic System.out/err.println to Apache log4j2. Added several "trace"-level logging statements to aid debugging which can be activated given appropriate configuration in log4j2.properties. With voting, detection of previous votes was broken during orphan as previousOptionIndex was set to 0 instead of null when fetching a VoteOnPollTransaction from the HSQLDB repository. This was due to lack of null-checking - now fixed. Corresponding changes made in IssueAsset and Message transaction HSQLDB repository classes. v1feeder now syncs up to block 99055. Block 99056 contains DeployAT. Orphaning back to block 1 and then resync works without issue too.
This commit is contained in:
parent
e56d8f5e02
commit
ad250e57c8
@ -2,6 +2,9 @@ package qora.account;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import data.account.AccountBalanceData;
|
import data.account.AccountBalanceData;
|
||||||
import data.account.AccountData;
|
import data.account.AccountData;
|
||||||
import data.block.BlockData;
|
import data.block.BlockData;
|
||||||
@ -15,6 +18,8 @@ import repository.Repository;
|
|||||||
|
|
||||||
public class Account {
|
public class Account {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(Account.class);
|
||||||
|
|
||||||
public static final int ADDRESS_LENGTH = 25;
|
public static final int ADDRESS_LENGTH = 25;
|
||||||
|
|
||||||
protected Repository repository;
|
protected Repository repository;
|
||||||
@ -128,6 +133,8 @@ public class Account {
|
|||||||
public void setConfirmedBalance(long assetId, BigDecimal balance) throws DataException {
|
public void setConfirmedBalance(long assetId, BigDecimal balance) throws DataException {
|
||||||
AccountBalanceData accountBalanceData = new AccountBalanceData(this.accountData.getAddress(), assetId, balance);
|
AccountBalanceData accountBalanceData = new AccountBalanceData(this.accountData.getAddress(), assetId, balance);
|
||||||
this.repository.getAccountRepository().save(accountBalanceData);
|
this.repository.getAccountRepository().save(accountBalanceData);
|
||||||
|
|
||||||
|
LOGGER.trace(this.accountData.getAddress() + " balance now: " + balance.toPlainString() + " [assetId " + assetId + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteBalance(long assetId) throws DataException {
|
public void deleteBalance(long assetId) throws DataException {
|
||||||
|
@ -5,6 +5,11 @@ import java.math.BigInteger;
|
|||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import com.google.common.hash.HashCode;
|
||||||
|
|
||||||
import data.assets.AssetData;
|
import data.assets.AssetData;
|
||||||
import data.assets.OrderData;
|
import data.assets.OrderData;
|
||||||
import data.assets.TradeData;
|
import data.assets.TradeData;
|
||||||
@ -16,6 +21,8 @@ import repository.Repository;
|
|||||||
|
|
||||||
public class Order {
|
public class Order {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(Order.class);
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
private Repository repository;
|
private Repository repository;
|
||||||
private OrderData orderData;
|
private OrderData orderData;
|
||||||
@ -104,10 +111,14 @@ public class Order {
|
|||||||
this.repository.getAssetRepository().save(this.orderData);
|
this.repository.getAssetRepository().save(this.orderData);
|
||||||
|
|
||||||
// Attempt to match orders
|
// Attempt to match orders
|
||||||
|
LOGGER.debug("Processing our order " + HashCode.fromBytes(this.orderData.getOrderId()).toString());
|
||||||
|
LOGGER.trace("We have: " + this.orderData.getAmount().toPlainString() + " " + haveAssetData.getName());
|
||||||
|
LOGGER.trace("We want " + this.orderData.getPrice().toPlainString() + " " + wantAssetData.getName() + " per " + haveAssetData.getName());
|
||||||
|
|
||||||
// Fetch corresponding open orders that might potentially match, hence reversed want/have assetId args.
|
// Fetch corresponding open orders that might potentially match, hence reversed want/have assetId args.
|
||||||
// Returned orders are sorted with lowest "price" first.
|
// Returned orders are sorted with lowest "price" first.
|
||||||
List<OrderData> orders = assetRepository.getOpenOrders(wantAssetId, haveAssetId);
|
List<OrderData> orders = assetRepository.getOpenOrders(wantAssetId, haveAssetId);
|
||||||
|
LOGGER.trace("Open orders fetched from repository: " + orders.size());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Our order example:
|
* Our order example:
|
||||||
@ -123,6 +134,11 @@ public class Order {
|
|||||||
BigDecimal ourPrice = this.orderData.getPrice();
|
BigDecimal ourPrice = this.orderData.getPrice();
|
||||||
|
|
||||||
for (OrderData theirOrderData : orders) {
|
for (OrderData theirOrderData : orders) {
|
||||||
|
LOGGER.trace("Considering order " + HashCode.fromBytes(theirOrderData.getOrderId()).toString());
|
||||||
|
// Note swapped use of have/want asset data as this is from 'their' perspective.
|
||||||
|
LOGGER.trace("They have: " + theirOrderData.getAmount().toPlainString() + " " + wantAssetData.getName());
|
||||||
|
LOGGER.trace("They want " + theirOrderData.getPrice().toPlainString() + " " + haveAssetData.getName() + " per " + wantAssetData.getName());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Potential matching order example:
|
* Potential matching order example:
|
||||||
*
|
*
|
||||||
@ -137,6 +153,7 @@ public class Order {
|
|||||||
|
|
||||||
// Round down otherwise their buyingPrice would be better than advertised and cause issues
|
// Round down otherwise their buyingPrice would be better than advertised and cause issues
|
||||||
BigDecimal theirBuyingPrice = BigDecimal.ONE.setScale(8).divide(theirOrderData.getPrice(), RoundingMode.DOWN);
|
BigDecimal theirBuyingPrice = BigDecimal.ONE.setScale(8).divide(theirOrderData.getPrice(), RoundingMode.DOWN);
|
||||||
|
LOGGER.trace("theirBuyingPrice: " + theirBuyingPrice.toPlainString() + " " + wantAssetData.getName() + " per " + haveAssetData.getName());
|
||||||
|
|
||||||
// If their buyingPrice is less than what we're willing to pay then we're done as prices only get worse as we iterate through list of orders
|
// If their buyingPrice is less than what we're willing to pay then we're done as prices only get worse as we iterate through list of orders
|
||||||
if (theirBuyingPrice.compareTo(ourPrice) < 0)
|
if (theirBuyingPrice.compareTo(ourPrice) < 0)
|
||||||
@ -144,10 +161,13 @@ public class Order {
|
|||||||
|
|
||||||
// Calculate how many want-asset we could buy at their price
|
// Calculate how many want-asset we could buy at their price
|
||||||
BigDecimal ourAmountLeft = this.getAmountLeft().multiply(theirBuyingPrice).setScale(8, RoundingMode.DOWN);
|
BigDecimal ourAmountLeft = this.getAmountLeft().multiply(theirBuyingPrice).setScale(8, RoundingMode.DOWN);
|
||||||
// How many want-asset is left available in this order
|
LOGGER.trace("ourAmountLeft (max we could buy at their price): " + ourAmountLeft.toPlainString() + " " + wantAssetData.getName());
|
||||||
|
// How many want-asset is remaining available in this order
|
||||||
BigDecimal theirAmountLeft = Order.getAmountLeft(theirOrderData);
|
BigDecimal theirAmountLeft = Order.getAmountLeft(theirOrderData);
|
||||||
|
LOGGER.trace("theirAmountLeft (max amount remaining in order): " + theirAmountLeft.toPlainString() + " " + wantAssetData.getName());
|
||||||
// So matchable want-asset amount is the minimum of above two values
|
// So matchable want-asset amount is the minimum of above two values
|
||||||
BigDecimal matchedAmount = ourAmountLeft.min(theirAmountLeft);
|
BigDecimal matchedAmount = ourAmountLeft.min(theirAmountLeft);
|
||||||
|
LOGGER.trace("matchedAmount: " + matchedAmount.toPlainString() + " " + wantAssetData.getName());
|
||||||
|
|
||||||
// If we can't buy anything then we're done
|
// If we can't buy anything then we're done
|
||||||
if (matchedAmount.compareTo(BigDecimal.ZERO) <= 0)
|
if (matchedAmount.compareTo(BigDecimal.ZERO) <= 0)
|
||||||
@ -155,7 +175,9 @@ public class Order {
|
|||||||
|
|
||||||
// Calculate amount granularity based on both assets' divisibility
|
// Calculate amount granularity based on both assets' divisibility
|
||||||
BigDecimal increment = this.calculateAmountGranularity(haveAssetData, wantAssetData, theirOrderData);
|
BigDecimal increment = this.calculateAmountGranularity(haveAssetData, wantAssetData, theirOrderData);
|
||||||
|
LOGGER.trace("increment (want-asset amount granularity): " + increment.toPlainString() + " " + wantAssetData.getName());
|
||||||
matchedAmount = matchedAmount.subtract(matchedAmount.remainder(increment));
|
matchedAmount = matchedAmount.subtract(matchedAmount.remainder(increment));
|
||||||
|
LOGGER.trace("matchedAmount adjusted for granularity: " + matchedAmount.toPlainString() + " " + wantAssetData.getName());
|
||||||
|
|
||||||
// If we can't buy anything then we're done
|
// If we can't buy anything then we're done
|
||||||
if (matchedAmount.compareTo(BigDecimal.ZERO) <= 0)
|
if (matchedAmount.compareTo(BigDecimal.ZERO) <= 0)
|
||||||
@ -165,6 +187,7 @@ public class Order {
|
|||||||
|
|
||||||
// Calculate the total cost to us, in have-asset, based on their price
|
// Calculate the total cost to us, in have-asset, based on their price
|
||||||
BigDecimal tradePrice = matchedAmount.multiply(theirOrderData.getPrice()).setScale(8);
|
BigDecimal tradePrice = matchedAmount.multiply(theirOrderData.getPrice()).setScale(8);
|
||||||
|
LOGGER.trace("tradePrice ('want' trade agreed): " + tradePrice.toPlainString() + " " + haveAssetData.getName());
|
||||||
|
|
||||||
// Construct trade
|
// Construct trade
|
||||||
TradeData tradeData = new TradeData(this.orderData.getOrderId(), theirOrderData.getOrderId(), matchedAmount, tradePrice,
|
TradeData tradeData = new TradeData(this.orderData.getOrderId(), theirOrderData.getOrderId(), matchedAmount, tradePrice,
|
||||||
@ -175,8 +198,12 @@ public class Order {
|
|||||||
|
|
||||||
// Update our order in terms of fulfilment, etc. but do not save into repository as that's handled by Trade above
|
// Update our order in terms of fulfilment, etc. but do not save into repository as that's handled by Trade above
|
||||||
this.orderData.setFulfilled(this.orderData.getFulfilled().add(tradePrice));
|
this.orderData.setFulfilled(this.orderData.getFulfilled().add(tradePrice));
|
||||||
|
LOGGER.trace("Updated our order's fulfilled amount to: " + this.orderData.getFulfilled().toPlainString() + " " + haveAssetData.getName());
|
||||||
|
LOGGER.trace("Our order's amount remaining: " + this.getAmountLeft().toPlainString() + " " + haveAssetData.getName());
|
||||||
|
|
||||||
// Continue on to process other open orders in case we still have amount left to match
|
// Continue on to process other open orders if we still have amount left to match
|
||||||
|
if (this.getAmountLeft().compareTo(BigDecimal.ZERO) <= 0)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,9 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
|
|
||||||
import data.block.BlockData;
|
import data.block.BlockData;
|
||||||
@ -80,6 +83,7 @@ public class Block {
|
|||||||
protected PublicKeyAccount generator;
|
protected PublicKeyAccount generator;
|
||||||
|
|
||||||
// Other properties
|
// Other properties
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(Block.class);
|
||||||
protected List<Transaction> transactions;
|
protected List<Transaction> transactions;
|
||||||
protected BigDecimal cachedNextGeneratingBalance;
|
protected BigDecimal cachedNextGeneratingBalance;
|
||||||
|
|
||||||
@ -565,7 +569,7 @@ public class Block {
|
|||||||
// NOTE: in Gen1 there was an extra block height passed to DeployATTransaction.isValid
|
// NOTE: in Gen1 there was an extra block height passed to DeployATTransaction.isValid
|
||||||
Transaction.ValidationResult validationResult = transaction.isValid();
|
Transaction.ValidationResult validationResult = transaction.isValid();
|
||||||
if (validationResult != Transaction.ValidationResult.OK) {
|
if (validationResult != Transaction.ValidationResult.OK) {
|
||||||
System.err.println("Error during transaction validation, tx " + Base58.encode(transaction.getTransactionData().getSignature()) + ": "
|
LOGGER.error("Error during transaction validation, tx " + Base58.encode(transaction.getTransactionData().getSignature()) + ": "
|
||||||
+ validationResult.value);
|
+ validationResult.value);
|
||||||
return ValidationResult.TRANSACTION_INVALID;
|
return ValidationResult.TRANSACTION_INVALID;
|
||||||
}
|
}
|
||||||
@ -574,9 +578,7 @@ public class Block {
|
|||||||
try {
|
try {
|
||||||
transaction.process();
|
transaction.process();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// LOGGER.error("Exception during transaction validation, tx " + Base58.encode(transaction.getSignature()), e);
|
LOGGER.error("Exception during transaction validation, tx " + Base58.encode(transaction.getTransactionData().getSignature()), e);
|
||||||
System.err.println("Exception during transaction validation, tx " + Base58.encode(transaction.getTransactionData().getSignature()) + ": "
|
|
||||||
+ e.getMessage());
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return ValidationResult.TRANSACTION_PROCESSING_FAILED;
|
return ValidationResult.TRANSACTION_PROCESSING_FAILED;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,8 @@ public class Payment {
|
|||||||
return isValid(senderPublicKey, paymentData, fee, false);
|
return isValid(senderPublicKey, paymentData, fee, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, byte[] signature) throws DataException {
|
public void process(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, byte[] signature, boolean alwaysInitializeRecipientReference)
|
||||||
|
throws DataException {
|
||||||
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
|
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
|
||||||
|
|
||||||
// Update sender's balance due to fee
|
// Update sender's balance due to fee
|
||||||
@ -117,16 +118,18 @@ public class Payment {
|
|||||||
recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).add(amount));
|
recipient.setConfirmedBalance(assetId, recipient.getConfirmedBalance(assetId).add(amount));
|
||||||
|
|
||||||
// For QORA amounts only: if recipient has no reference yet, then this is their starting reference
|
// For QORA amounts only: if recipient has no reference yet, then this is their starting reference
|
||||||
if (assetId == Asset.QORA && recipient.getLastReference() == null)
|
if ((alwaysInitializeRecipientReference || assetId == Asset.QORA) && recipient.getLastReference() == null)
|
||||||
recipient.setLastReference(signature);
|
recipient.setLastReference(signature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature) throws DataException {
|
public void process(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature, boolean alwaysInitializeRecipientReference)
|
||||||
process(senderPublicKey, Collections.singletonList(paymentData), fee, signature);
|
throws DataException {
|
||||||
|
process(senderPublicKey, Collections.singletonList(paymentData), fee, signature, alwaysInitializeRecipientReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void orphan(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, byte[] signature, byte[] reference) throws DataException {
|
public void orphan(byte[] senderPublicKey, List<PaymentData> payments, BigDecimal fee, byte[] signature, byte[] reference,
|
||||||
|
boolean alwaysUninitializeRecipientReference) throws DataException {
|
||||||
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
|
Account sender = new PublicKeyAccount(this.repository, senderPublicKey);
|
||||||
|
|
||||||
// Update sender's balance due to fee
|
// Update sender's balance due to fee
|
||||||
@ -152,13 +155,14 @@ public class Payment {
|
|||||||
* For QORA amounts only: If recipient's last reference is this transaction's signature, then they can't have made any transactions of their own
|
* For QORA amounts only: If recipient's last reference is this transaction's signature, then they can't have made any transactions of their own
|
||||||
* (which would have changed their last reference) thus this is their first reference so remove it.
|
* (which would have changed their last reference) thus this is their first reference so remove it.
|
||||||
*/
|
*/
|
||||||
if (assetId == Asset.QORA && Arrays.equals(recipient.getLastReference(), signature))
|
if ((alwaysUninitializeRecipientReference || assetId == Asset.QORA) && Arrays.equals(recipient.getLastReference(), signature))
|
||||||
recipient.setLastReference(null);
|
recipient.setLastReference(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void orphan(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature, byte[] reference) throws DataException {
|
public void orphan(byte[] senderPublicKey, PaymentData paymentData, BigDecimal fee, byte[] signature, byte[] reference,
|
||||||
orphan(senderPublicKey, Collections.singletonList(paymentData), fee, signature, reference);
|
boolean alwaysUninitializeRecipientReference) throws DataException {
|
||||||
|
orphan(senderPublicKey, Collections.singletonList(paymentData), fee, signature, reference, alwaysUninitializeRecipientReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -166,9 +166,9 @@ public class ArbitraryTransaction extends Transaction {
|
|||||||
// Save this transaction itself
|
// Save this transaction itself
|
||||||
this.repository.getTransactionRepository().save(this.transactionData);
|
this.repository.getTransactionRepository().save(this.transactionData);
|
||||||
|
|
||||||
// Wrap and delegate payment processing to Payment class
|
// Wrap and delegate payment processing to Payment class. Always update recipients' last references regardless of asset.
|
||||||
new Payment(this.repository).process(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
|
new Payment(this.repository).process(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
|
||||||
arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature());
|
arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -190,9 +190,9 @@ public class ArbitraryTransaction extends Transaction {
|
|||||||
// Delete this transaction itself
|
// Delete this transaction itself
|
||||||
this.repository.getTransactionRepository().delete(this.transactionData);
|
this.repository.getTransactionRepository().delete(this.transactionData);
|
||||||
|
|
||||||
// Wrap and delegate payment processing to Payment class
|
// Wrap and delegate payment processing to Payment class. Always revert recipients' last references regardless of asset.
|
||||||
new Payment(this.repository).orphan(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
|
new Payment(this.repository).orphan(arbitraryTransactionData.getSenderPublicKey(), arbitraryTransactionData.getPayments(),
|
||||||
arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature(), arbitraryTransactionData.getReference());
|
arbitraryTransactionData.getFee(), arbitraryTransactionData.getSignature(), arbitraryTransactionData.getReference(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -119,9 +119,9 @@ public class MessageTransaction extends Transaction {
|
|||||||
// Save this transaction itself
|
// Save this transaction itself
|
||||||
this.repository.getTransactionRepository().save(this.transactionData);
|
this.repository.getTransactionRepository().save(this.transactionData);
|
||||||
|
|
||||||
// Wrap and delegate payment processing to Payment class
|
// Wrap and delegate payment processing to Payment class. Only update recipient's last reference if transferring QORA.
|
||||||
new Payment(this.repository).process(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(),
|
new Payment(this.repository).process(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(),
|
||||||
messageTransactionData.getSignature());
|
messageTransactionData.getSignature(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -129,9 +129,9 @@ public class MessageTransaction extends Transaction {
|
|||||||
// Delete this transaction itself
|
// Delete this transaction itself
|
||||||
this.repository.getTransactionRepository().delete(this.transactionData);
|
this.repository.getTransactionRepository().delete(this.transactionData);
|
||||||
|
|
||||||
// Wrap and delegate payment processing to Payment class
|
// Wrap and delegate payment processing to Payment class. Only revert recipient's last reference if transferring QORA.
|
||||||
new Payment(this.repository).orphan(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(),
|
new Payment(this.repository).orphan(messageTransactionData.getSenderPublicKey(), getPaymentData(), messageTransactionData.getFee(),
|
||||||
messageTransactionData.getSignature(), messageTransactionData.getReference());
|
messageTransactionData.getSignature(), messageTransactionData.getReference(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -119,9 +119,9 @@ public class MultiPaymentTransaction extends Transaction {
|
|||||||
// Save this transaction itself
|
// Save this transaction itself
|
||||||
this.repository.getTransactionRepository().save(this.transactionData);
|
this.repository.getTransactionRepository().save(this.transactionData);
|
||||||
|
|
||||||
// Wrap and delegate payment processing to Payment class
|
// Wrap and delegate payment processing to Payment class. Always update recipients' last references regardless of asset.
|
||||||
new Payment(this.repository).process(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(),
|
new Payment(this.repository).process(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(),
|
||||||
multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature());
|
multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -129,9 +129,9 @@ public class MultiPaymentTransaction extends Transaction {
|
|||||||
// Delete this transaction itself
|
// Delete this transaction itself
|
||||||
this.repository.getTransactionRepository().delete(this.transactionData);
|
this.repository.getTransactionRepository().delete(this.transactionData);
|
||||||
|
|
||||||
// Wrap and delegate payment processing to Payment class
|
// Wrap and delegate payment processing to Payment class. Always revert recipients' last references regardless of asset.
|
||||||
new Payment(this.repository).orphan(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(),
|
new Payment(this.repository).orphan(multiPaymentTransactionData.getSenderPublicKey(), multiPaymentTransactionData.getPayments(),
|
||||||
multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), multiPaymentTransactionData.getReference());
|
multiPaymentTransactionData.getFee(), multiPaymentTransactionData.getSignature(), multiPaymentTransactionData.getReference(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -91,9 +91,9 @@ public class PaymentTransaction extends Transaction {
|
|||||||
// Save this transaction itself
|
// Save this transaction itself
|
||||||
this.repository.getTransactionRepository().save(this.transactionData);
|
this.repository.getTransactionRepository().save(this.transactionData);
|
||||||
|
|
||||||
// Wrap and delegate payment processing to Payment class
|
// Wrap and delegate payment processing to Payment class. Only update recipient's last reference if transferring QORA.
|
||||||
new Payment(this.repository).process(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(),
|
new Payment(this.repository).process(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(),
|
||||||
paymentTransactionData.getSignature());
|
paymentTransactionData.getSignature(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -101,9 +101,9 @@ public class PaymentTransaction extends Transaction {
|
|||||||
// Delete this transaction
|
// Delete this transaction
|
||||||
this.repository.getTransactionRepository().delete(this.transactionData);
|
this.repository.getTransactionRepository().delete(this.transactionData);
|
||||||
|
|
||||||
// Wrap and delegate payment processing to Payment class
|
// Wrap and delegate payment processing to Payment class. Only revert recipient's last reference if transferring QORA.
|
||||||
new Payment(this.repository).orphan(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(),
|
new Payment(this.repository).orphan(paymentTransactionData.getSenderPublicKey(), getPaymentData(), paymentTransactionData.getFee(),
|
||||||
paymentTransactionData.getSignature(), paymentTransactionData.getReference());
|
paymentTransactionData.getSignature(), paymentTransactionData.getReference(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -104,9 +104,9 @@ public class TransferAssetTransaction extends Transaction {
|
|||||||
// Save this transaction itself
|
// Save this transaction itself
|
||||||
this.repository.getTransactionRepository().save(this.transactionData);
|
this.repository.getTransactionRepository().save(this.transactionData);
|
||||||
|
|
||||||
// Wrap asset transfer as a payment and delegate processing to Payment class
|
// Wrap asset transfer as a payment and delegate processing to Payment class. Only update recipient's last reference if transferring QORA.
|
||||||
new Payment(this.repository).process(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(),
|
new Payment(this.repository).process(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(),
|
||||||
transferAssetTransactionData.getSignature());
|
transferAssetTransactionData.getSignature(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -114,9 +114,9 @@ public class TransferAssetTransaction extends Transaction {
|
|||||||
// Delete this transaction itself
|
// Delete this transaction itself
|
||||||
this.repository.getTransactionRepository().delete(this.transactionData);
|
this.repository.getTransactionRepository().delete(this.transactionData);
|
||||||
|
|
||||||
// Wrap asset transfer as a payment and delegate processing to Payment class
|
// Wrap asset transfer as a payment and delegate processing to Payment class. Only revert recipient's last reference if transferring QORA.
|
||||||
new Payment(this.repository).orphan(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(),
|
new Payment(this.repository).orphan(transferAssetTransactionData.getSenderPublicKey(), getPaymentData(), transferAssetTransactionData.getFee(),
|
||||||
transferAssetTransactionData.getSignature(), transferAssetTransactionData.getReference());
|
transferAssetTransactionData.getSignature(), transferAssetTransactionData.getReference(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,9 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import com.google.common.base.Utf8;
|
import com.google.common.base.Utf8;
|
||||||
|
|
||||||
import data.transaction.TransactionData;
|
import data.transaction.TransactionData;
|
||||||
@ -23,6 +26,8 @@ import repository.VotingRepository;
|
|||||||
|
|
||||||
public class VoteOnPollTransaction extends Transaction {
|
public class VoteOnPollTransaction extends Transaction {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(VoteOnPollTransaction.class);
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
private VoteOnPollTransactionData voteOnPollTransactionData;
|
private VoteOnPollTransactionData voteOnPollTransactionData;
|
||||||
|
|
||||||
@ -130,13 +135,18 @@ public class VoteOnPollTransaction extends Transaction {
|
|||||||
// Check for previous vote so we can save option in case of orphaning
|
// Check for previous vote so we can save option in case of orphaning
|
||||||
VoteOnPollData previousVoteOnPollData = votingRepository.getVote(voteOnPollTransactionData.getPollName(),
|
VoteOnPollData previousVoteOnPollData = votingRepository.getVote(voteOnPollTransactionData.getPollName(),
|
||||||
voteOnPollTransactionData.getVoterPublicKey());
|
voteOnPollTransactionData.getVoterPublicKey());
|
||||||
if (previousVoteOnPollData != null)
|
if (previousVoteOnPollData != null) {
|
||||||
voteOnPollTransactionData.setPreviousOptionIndex(previousVoteOnPollData.getOptionIndex());
|
voteOnPollTransactionData.setPreviousOptionIndex(previousVoteOnPollData.getOptionIndex());
|
||||||
|
LOGGER.trace("Previous vote by " + voter.getAddress() + " on poll \"" + voteOnPollTransactionData.getPollName() + "\" was option index "
|
||||||
|
+ previousVoteOnPollData.getOptionIndex());
|
||||||
|
}
|
||||||
|
|
||||||
// Save this transaction, now with possible previous vote
|
// Save this transaction, now with possible previous vote
|
||||||
this.repository.getTransactionRepository().save(voteOnPollTransactionData);
|
this.repository.getTransactionRepository().save(voteOnPollTransactionData);
|
||||||
|
|
||||||
// Apply vote to poll
|
// Apply vote to poll
|
||||||
|
LOGGER.trace("Vote by " + voter.getAddress() + " on poll \"" + voteOnPollTransactionData.getPollName() + "\" with option index "
|
||||||
|
+ voteOnPollTransactionData.getOptionIndex());
|
||||||
VoteOnPollData newVoteOnPollData = new VoteOnPollData(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey(),
|
VoteOnPollData newVoteOnPollData = new VoteOnPollData(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey(),
|
||||||
voteOnPollTransactionData.getOptionIndex());
|
voteOnPollTransactionData.getOptionIndex());
|
||||||
votingRepository.save(newVoteOnPollData);
|
votingRepository.save(newVoteOnPollData);
|
||||||
@ -156,11 +166,15 @@ public class VoteOnPollTransaction extends Transaction {
|
|||||||
Integer previousOptionIndex = voteOnPollTransactionData.getPreviousOptionIndex();
|
Integer previousOptionIndex = voteOnPollTransactionData.getPreviousOptionIndex();
|
||||||
if (previousOptionIndex != null) {
|
if (previousOptionIndex != null) {
|
||||||
// Reinstate previous vote
|
// Reinstate previous vote
|
||||||
|
LOGGER.trace("Reinstating previous vote by " + voter.getAddress() + " on poll \"" + voteOnPollTransactionData.getPollName()
|
||||||
|
+ "\" with option index " + previousOptionIndex);
|
||||||
VoteOnPollData previousVoteOnPollData = new VoteOnPollData(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey(),
|
VoteOnPollData previousVoteOnPollData = new VoteOnPollData(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey(),
|
||||||
previousOptionIndex);
|
previousOptionIndex);
|
||||||
votingRepository.save(previousVoteOnPollData);
|
votingRepository.save(previousVoteOnPollData);
|
||||||
} else {
|
} else {
|
||||||
// Delete vote
|
// Delete vote
|
||||||
|
LOGGER.trace("Deleting vote by " + voter.getAddress() + " on poll \"" + voteOnPollTransactionData.getPollName() + "\" with option index "
|
||||||
|
+ voteOnPollTransactionData.getOptionIndex());
|
||||||
votingRepository.delete(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey());
|
votingRepository.delete(voteOnPollTransactionData.getPollName(), voteOnPollTransactionData.getVoterPublicKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +148,8 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
|||||||
|
|
||||||
try (ResultSet resultSet = this.repository.checkedExecute(
|
try (ResultSet resultSet = this.repository.checkedExecute(
|
||||||
"SELECT creator, asset_order_id, amount, fulfilled, price, ordered FROM AssetOrders "
|
"SELECT creator, asset_order_id, amount, fulfilled, price, ordered FROM AssetOrders "
|
||||||
+ "WHERE have_asset_id = ? AND want_asset_id = ? AND is_closed = FALSE AND is_fulfilled = FALSE ORDER BY price ASC",
|
+ "WHERE have_asset_id = ? AND want_asset_id = ? AND is_closed = FALSE AND is_fulfilled = FALSE "
|
||||||
|
+ "ORDER BY price ASC, ordered ASC",
|
||||||
haveAssetId, wantAssetId)) {
|
haveAssetId, wantAssetId)) {
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return orders;
|
return orders;
|
||||||
|
@ -309,7 +309,7 @@ public class HSQLDBDatabaseUpdates {
|
|||||||
+ "ordered TIMESTAMP WITH TIME ZONE NOT NULL, is_closed BOOLEAN NOT NULL, is_fulfilled BOOLEAN NOT NULL, "
|
+ "ordered TIMESTAMP WITH TIME ZONE NOT NULL, is_closed BOOLEAN NOT NULL, is_fulfilled BOOLEAN NOT NULL, "
|
||||||
+ "PRIMARY KEY (asset_order_id))");
|
+ "PRIMARY KEY (asset_order_id))");
|
||||||
// For quick matching of orders. is_closed are is_fulfilled included so inactive orders can be filtered out.
|
// For quick matching of orders. is_closed are is_fulfilled included so inactive orders can be filtered out.
|
||||||
stmt.execute("CREATE INDEX AssetOrderMatchingIndex on AssetOrders (have_asset_id, want_asset_id, is_closed, is_fulfilled)");
|
stmt.execute("CREATE INDEX AssetOrderMatchingIndex on AssetOrders (have_asset_id, want_asset_id, is_closed, is_fulfilled, price, ordered)");
|
||||||
// For when a user wants to look up their current/historic orders. is_closed included so user can filter by active/inactive orders.
|
// For when a user wants to look up their current/historic orders. is_closed included so user can filter by active/inactive orders.
|
||||||
stmt.execute("CREATE INDEX AssetOrderCreatorIndex on AssetOrders (creator, is_closed)");
|
stmt.execute("CREATE INDEX AssetOrderCreatorIndex on AssetOrders (creator, is_closed)");
|
||||||
break;
|
break;
|
||||||
|
@ -8,6 +8,9 @@ import java.sql.SQLException;
|
|||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import repository.AccountRepository;
|
import repository.AccountRepository;
|
||||||
import repository.AssetRepository;
|
import repository.AssetRepository;
|
||||||
import repository.BlockRepository;
|
import repository.BlockRepository;
|
||||||
@ -20,6 +23,8 @@ import repository.hsqldb.transaction.HSQLDBTransactionRepository;
|
|||||||
|
|
||||||
public class HSQLDBRepository implements Repository {
|
public class HSQLDBRepository implements Repository {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(HSQLDBRepository.class);
|
||||||
|
|
||||||
public static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
public static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||||
|
|
||||||
protected Connection connection;
|
protected Connection connection;
|
||||||
@ -86,12 +91,12 @@ public class HSQLDBRepository implements Repository {
|
|||||||
|
|
||||||
try (ResultSet resultSet = stmt.getResultSet()) {
|
try (ResultSet resultSet = stmt.getResultSet()) {
|
||||||
if (resultSet == null || !resultSet.next())
|
if (resultSet == null || !resultSet.next())
|
||||||
System.out.println("Unable to check repository status during close");
|
LOGGER.warn("Unable to check repository status during close");
|
||||||
|
|
||||||
boolean inTransaction = resultSet.getBoolean(1);
|
boolean inTransaction = resultSet.getBoolean(1);
|
||||||
int transactionCount = resultSet.getInt(2);
|
int transactionCount = resultSet.getInt(2);
|
||||||
if (inTransaction && transactionCount != 0)
|
if (inTransaction && transactionCount != 0)
|
||||||
System.out.println("Uncommitted changes (" + transactionCount + ") during repository close");
|
LOGGER.warn("Uncommitted changes (" + transactionCount + ") during repository close");
|
||||||
}
|
}
|
||||||
|
|
||||||
// give connection back to the pool
|
// give connection back to the pool
|
||||||
|
@ -28,7 +28,11 @@ public class HSQLDBIssueAssetTransactionRepository extends HSQLDBTransactionRepo
|
|||||||
String description = resultSet.getString(4);
|
String description = resultSet.getString(4);
|
||||||
long quantity = resultSet.getLong(5);
|
long quantity = resultSet.getLong(5);
|
||||||
boolean isDivisible = resultSet.getBoolean(6);
|
boolean isDivisible = resultSet.getBoolean(6);
|
||||||
|
|
||||||
|
// Special null-checking for asset ID
|
||||||
Long assetId = resultSet.getLong(7);
|
Long assetId = resultSet.getLong(7);
|
||||||
|
if (resultSet.wasNull())
|
||||||
|
assetId = null;
|
||||||
|
|
||||||
return new IssueAssetTransactionData(assetId, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference,
|
return new IssueAssetTransactionData(assetId, issuerPublicKey, owner, assetName, description, quantity, isDivisible, fee, timestamp, reference,
|
||||||
signature);
|
signature);
|
||||||
|
@ -28,7 +28,12 @@ public class HSQLDBMessageTransactionRepository extends HSQLDBTransactionReposit
|
|||||||
boolean isText = resultSet.getBoolean(4);
|
boolean isText = resultSet.getBoolean(4);
|
||||||
boolean isEncrypted = resultSet.getBoolean(5);
|
boolean isEncrypted = resultSet.getBoolean(5);
|
||||||
BigDecimal amount = resultSet.getBigDecimal(6);
|
BigDecimal amount = resultSet.getBigDecimal(6);
|
||||||
|
|
||||||
|
// Special null-checking for asset ID
|
||||||
Long assetId = resultSet.getLong(7);
|
Long assetId = resultSet.getLong(7);
|
||||||
|
if (resultSet.wasNull())
|
||||||
|
assetId = null;
|
||||||
|
|
||||||
byte[] data = resultSet.getBytes(8);
|
byte[] data = resultSet.getBytes(8);
|
||||||
|
|
||||||
return new MessageTransactionData(version, senderPublicKey, recipient, assetId, amount, data, isText, isEncrypted, fee, timestamp, reference,
|
return new MessageTransactionData(version, senderPublicKey, recipient, assetId, amount, data, isText, isEncrypted, fee, timestamp, reference,
|
||||||
|
@ -24,7 +24,11 @@ public class HSQLDBVoteOnPollTransactionRepository extends HSQLDBTransactionRepo
|
|||||||
|
|
||||||
String pollName = resultSet.getString(1);
|
String pollName = resultSet.getString(1);
|
||||||
int optionIndex = resultSet.getInt(2);
|
int optionIndex = resultSet.getInt(2);
|
||||||
|
|
||||||
|
// Special null-checking for previous option index
|
||||||
Integer previousOptionIndex = resultSet.getInt(3);
|
Integer previousOptionIndex = resultSet.getInt(3);
|
||||||
|
if (resultSet.wasNull())
|
||||||
|
previousOptionIndex = null;
|
||||||
|
|
||||||
return new VoteOnPollTransactionData(creatorPublicKey, pollName, optionIndex, previousOptionIndex, fee, timestamp, reference, signature);
|
return new VoteOnPollTransactionData(creatorPublicKey, pollName, optionIndex, previousOptionIndex, fee, timestamp, reference, signature);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
@ -157,8 +157,6 @@ public class CreatePollTransactionTransformer extends TransactionTransformer {
|
|||||||
// Replace transaction type with incorrect Register Name value
|
// Replace transaction type with incorrect Register Name value
|
||||||
System.arraycopy(Ints.toByteArray(TransactionType.REGISTER_NAME.value), 0, bytes, 0, INT_LENGTH);
|
System.arraycopy(Ints.toByteArray(TransactionType.REGISTER_NAME.value), 0, bytes, 0, INT_LENGTH);
|
||||||
|
|
||||||
System.out.println(HashCode.fromBytes(bytes).toString());
|
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ import java.nio.BufferUnderflowException;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.json.simple.JSONObject;
|
import org.json.simple.JSONObject;
|
||||||
|
|
||||||
import com.google.common.hash.HashCode;
|
import com.google.common.hash.HashCode;
|
||||||
@ -18,6 +20,8 @@ import utils.Base58;
|
|||||||
|
|
||||||
public class TransactionTransformer extends Transformer {
|
public class TransactionTransformer extends Transformer {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(TransactionTransformer.class);
|
||||||
|
|
||||||
protected static final int TYPE_LENGTH = INT_LENGTH;
|
protected static final int TYPE_LENGTH = INT_LENGTH;
|
||||||
protected static final int REFERENCE_LENGTH = SIGNATURE_LENGTH;
|
protected static final int REFERENCE_LENGTH = SIGNATURE_LENGTH;
|
||||||
protected static final int FEE_LENGTH = BIG_DECIMAL_LENGTH;
|
protected static final int FEE_LENGTH = BIG_DECIMAL_LENGTH;
|
||||||
@ -30,7 +34,7 @@ public class TransactionTransformer extends Transformer {
|
|||||||
if (bytes.length < TYPE_LENGTH)
|
if (bytes.length < TYPE_LENGTH)
|
||||||
throw new TransformationException("Byte data too short to determine transaction type");
|
throw new TransformationException("Byte data too short to determine transaction type");
|
||||||
|
|
||||||
System.out.println("v1 tx hex: " + HashCode.fromBytes(bytes).toString());
|
LOGGER.trace("tx hex: " + HashCode.fromBytes(bytes).toString());
|
||||||
|
|
||||||
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||||
|
|
||||||
|
@ -13,6 +13,9 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
|
|
||||||
import data.block.BlockData;
|
import data.block.BlockData;
|
||||||
@ -30,6 +33,8 @@ import utils.Pair;
|
|||||||
|
|
||||||
public class v1feeder extends Thread {
|
public class v1feeder extends Thread {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(v1feeder.class);
|
||||||
|
|
||||||
private static final int INACTIVITY_TIMEOUT = 60 * 1000; // milliseconds
|
private static final int INACTIVITY_TIMEOUT = 60 * 1000; // milliseconds
|
||||||
private static final int CONNECTION_TIMEOUT = 2 * 1000; // milliseconds
|
private static final int CONNECTION_TIMEOUT = 2 * 1000; // milliseconds
|
||||||
private static final int PING_INTERVAL = 10 * 1000; // milliseconds
|
private static final int PING_INTERVAL = 10 * 1000; // milliseconds
|
||||||
@ -86,11 +91,11 @@ public class v1feeder extends Thread {
|
|||||||
this.socket.connect(socketAddress, CONNECTION_TIMEOUT);
|
this.socket.connect(socketAddress, CONNECTION_TIMEOUT);
|
||||||
break;
|
break;
|
||||||
} catch (SocketTimeoutException e) {
|
} catch (SocketTimeoutException e) {
|
||||||
System.err.println("Timed out trying to connect to " + address + " - retrying");
|
LOGGER.info("Timed out trying to connect to " + address + " - retrying");
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
this.socket = null;
|
this.socket = null;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println("Failed to connect to " + address + ": " + e.getMessage());
|
LOGGER.error("Failed to connect to " + address, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,9 +115,9 @@ public class v1feeder extends Thread {
|
|||||||
// Start main communication thread
|
// Start main communication thread
|
||||||
this.start();
|
this.start();
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
System.err.println("Failed to set socket timeout for address " + address + ": " + e.getMessage());
|
LOGGER.error("Failed to set socket timeout for address " + address, e);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
System.err.println("Failed to get output stream for address " + address + ": " + e.getMessage());
|
LOGGER.error("Failed to get output stream for address " + address, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +150,7 @@ public class v1feeder extends Thread {
|
|||||||
bytes.write(data);
|
bytes.write(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// System.out.println("Creating message type [" + type + "] with " + (hasId ? "id [" + id + "]" : "no id") + " and data length " + data.length);
|
LOGGER.trace("Creating message type [" + type + "] with " + (hasId ? "id [" + id + "]" : "no id") + " and data length " + data.length);
|
||||||
|
|
||||||
return bytes.toByteArray();
|
return bytes.toByteArray();
|
||||||
}
|
}
|
||||||
@ -158,14 +163,14 @@ public class v1feeder extends Thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processMessage(int type, int id, byte[] data) throws IOException {
|
private void processMessage(int type, int id, byte[] data) throws IOException {
|
||||||
// System.out.println("Received message type [" + type + "] with id [" + id + "] and data length " + data.length);
|
LOGGER.trace("Received message type [" + type + "] with id [" + id + "] and data length " + data.length);
|
||||||
|
|
||||||
ByteBuffer byteBuffer = ByteBuffer.wrap(data);
|
ByteBuffer byteBuffer = ByteBuffer.wrap(data);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case HEIGHT_TYPE:
|
case HEIGHT_TYPE:
|
||||||
int height = byteBuffer.getInt();
|
int height = byteBuffer.getInt();
|
||||||
|
|
||||||
System.out.println("Peer height: " + height);
|
LOGGER.info("Peer height: " + height);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SIGNATURES_TYPE:
|
case SIGNATURES_TYPE:
|
||||||
@ -178,7 +183,7 @@ public class v1feeder extends Thread {
|
|||||||
signatures.add(signature);
|
signatures.add(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
// System.out.println("We now have " + signatures.size() + " signature(s) to process");
|
LOGGER.trace("We now have " + signatures.size() + " signature(s) to process");
|
||||||
|
|
||||||
feederState = HAVE_HEADERS_STATE;
|
feederState = HAVE_HEADERS_STATE;
|
||||||
break;
|
break;
|
||||||
@ -191,7 +196,7 @@ public class v1feeder extends Thread {
|
|||||||
// read block and process
|
// read block and process
|
||||||
int claimedHeight = byteBuffer.getInt();
|
int claimedHeight = byteBuffer.getInt();
|
||||||
|
|
||||||
System.out.println("Received block allegedly at height " + claimedHeight);
|
LOGGER.info("Received block allegedly at height " + claimedHeight);
|
||||||
|
|
||||||
byte[] blockBytes = new byte[byteBuffer.remaining()];
|
byte[] blockBytes = new byte[byteBuffer.remaining()];
|
||||||
byteBuffer.get(blockBytes);
|
byteBuffer.get(blockBytes);
|
||||||
@ -201,53 +206,54 @@ public class v1feeder extends Thread {
|
|||||||
try {
|
try {
|
||||||
blockInfo = BlockTransformer.fromBytes(blockBytes);
|
blockInfo = BlockTransformer.fromBytes(blockBytes);
|
||||||
} catch (TransformationException e) {
|
} catch (TransformationException e) {
|
||||||
System.err.println("Couldn't parse block bytes from peer: " + e.getMessage());
|
LOGGER.error("Couldn't parse block bytes from peer", e);
|
||||||
System.exit(3);
|
throw new RuntimeException("Couldn't parse block bytes from peer", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
Block block = new Block(repository, blockInfo.getA(), blockInfo.getB());
|
Block block = new Block(repository, blockInfo.getA(), blockInfo.getB());
|
||||||
|
|
||||||
if (!block.isSignatureValid()) {
|
if (!block.isSignatureValid()) {
|
||||||
System.err.println("Invalid block signature");
|
LOGGER.error("Invalid block signature");
|
||||||
System.exit(4);
|
throw new RuntimeException("Invalid block signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidationResult result = block.isValid();
|
ValidationResult result = block.isValid();
|
||||||
|
|
||||||
if (result != ValidationResult.OK) {
|
if (result != ValidationResult.OK) {
|
||||||
System.err.println("Invalid block, validation result code: " + result.value);
|
LOGGER.error("Invalid block, validation result code: " + result.value);
|
||||||
System.exit(4);
|
throw new RuntimeException("Invalid block, validation result code: " + result.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
block.process();
|
block.process();
|
||||||
repository.saveChanges();
|
repository.saveChanges();
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
System.err.println("Unable to process block: " + e.getMessage());
|
LOGGER.error("Unable to process block", e);
|
||||||
e.printStackTrace();
|
throw new RuntimeException("Unable to process block", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
feederState = HAVE_BLOCK_STATE;
|
feederState = HAVE_BLOCK_STATE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PING_TYPE:
|
case PING_TYPE:
|
||||||
// System.out.println("Sending pong for ping [" + id + "]");
|
LOGGER.trace("Sending pong for ping [" + id + "]");
|
||||||
byte[] pongMessage = createMessage(PING_TYPE, true, id, null);
|
byte[] pongMessage = createMessage(PING_TYPE, true, id, null);
|
||||||
sendMessage(pongMessage);
|
sendMessage(pongMessage);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VERSION_TYPE:
|
case VERSION_TYPE:
|
||||||
@SuppressWarnings("unused") long timestamp = byteBuffer.getLong();
|
@SuppressWarnings("unused")
|
||||||
|
long timestamp = byteBuffer.getLong();
|
||||||
int versionLength = byteBuffer.getInt();
|
int versionLength = byteBuffer.getInt();
|
||||||
byte[] versionBytes = new byte[versionLength];
|
byte[] versionBytes = new byte[versionLength];
|
||||||
byteBuffer.get(versionBytes);
|
byteBuffer.get(versionBytes);
|
||||||
String version = new String(versionBytes, Charset.forName("UTF-8"));
|
String version = new String(versionBytes, Charset.forName("UTF-8"));
|
||||||
|
|
||||||
System.out.println("Peer version info: " + version);
|
LOGGER.info("Peer version info: " + version);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
System.out.println("Discarding message type [" + type + "] with id [" + id + "] and data length " + data.length);
|
LOGGER.trace("Discarding message type [" + type + "] with id [" + id + "] and data length " + data.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,7 +315,7 @@ public class v1feeder extends Thread {
|
|||||||
// Send our height
|
// Send our height
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
int height = repository.getBlockRepository().getBlockchainHeight();
|
int height = repository.getBlockRepository().getBlockchainHeight();
|
||||||
System.out.println("Sending our height " + height + " to peer");
|
LOGGER.trace("Sending our height " + height + " to peer");
|
||||||
byte[] heightMessage = createMessage(HEIGHT_TYPE, false, null, Ints.toByteArray(height));
|
byte[] heightMessage = createMessage(HEIGHT_TYPE, false, null, Ints.toByteArray(height));
|
||||||
sendMessage(heightMessage);
|
sendMessage(heightMessage);
|
||||||
}
|
}
|
||||||
@ -321,7 +327,7 @@ public class v1feeder extends Thread {
|
|||||||
int numRead = in.read(buffer, bufferEnd, in.available());
|
int numRead = in.read(buffer, bufferEnd, in.available());
|
||||||
if (numRead == -1) {
|
if (numRead == -1) {
|
||||||
// input EOF
|
// input EOF
|
||||||
System.out.println("Socket EOF");
|
LOGGER.info("Socket EOF");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,11 +359,11 @@ public class v1feeder extends Thread {
|
|||||||
|
|
||||||
// done?
|
// done?
|
||||||
if (signature == null) {
|
if (signature == null) {
|
||||||
System.out.println("No last block in repository?");
|
LOGGER.warn("No last block in repository?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Requesting more signatures...");
|
LOGGER.trace("Requesting more signatures...");
|
||||||
byte[] getSignaturesMessage = createMessage(GET_SIGNATURES_TYPE, true, null, signature);
|
byte[] getSignaturesMessage = createMessage(GET_SIGNATURES_TYPE, true, null, signature);
|
||||||
sendMessage(getSignaturesMessage);
|
sendMessage(getSignaturesMessage);
|
||||||
feederState = AWAITING_HEADERS_STATE;
|
feederState = AWAITING_HEADERS_STATE;
|
||||||
@ -371,7 +377,7 @@ public class v1feeder extends Thread {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Requesting next block...");
|
LOGGER.trace("Requesting next block...");
|
||||||
signature = signatures.remove(0);
|
signature = signatures.remove(0);
|
||||||
this.messageId = (int) ((Math.random() * 1000000) + 1);
|
this.messageId = (int) ((Math.random() * 1000000) + 1);
|
||||||
byte[] getBlockMessage = createMessage(GET_BLOCK_TYPE, true, this.messageId, signature);
|
byte[] getBlockMessage = createMessage(GET_BLOCK_TYPE, true, this.messageId, signature);
|
||||||
@ -380,9 +386,9 @@ public class v1feeder extends Thread {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException | DataException e) {
|
} catch (IOException | DataException | RuntimeException e) {
|
||||||
// give up
|
// give up
|
||||||
System.err.println("Exiting due to: " + e.getMessage());
|
LOGGER.info("Exiting", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -401,14 +407,14 @@ public class v1feeder extends Thread {
|
|||||||
try {
|
try {
|
||||||
test.Common.setRepository();
|
test.Common.setRepository();
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
System.err.println("Couldn't connect to repository: " + e.getMessage());
|
LOGGER.error("Couldn't connect to repository", e);
|
||||||
System.exit(2);
|
System.exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
BlockChain.validate();
|
BlockChain.validate();
|
||||||
} catch (DataException e) {
|
} catch (DataException e) {
|
||||||
System.err.println("Couldn't validate repository: " + e.getMessage());
|
LOGGER.error("Couldn't validate repository", e);
|
||||||
System.exit(2);
|
System.exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,7 +428,7 @@ public class v1feeder extends Thread {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Exiting v1feeder");
|
LOGGER.info("Exiting v1feeder");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
test.Common.closeRepository();
|
test.Common.closeRepository();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user