From 00d07ab272a17a1959204ccd076e912df0844ba9 Mon Sep 17 00:00:00 2001 From: bokoto000 Date: Thu, 26 Oct 2023 09:02:23 +0300 Subject: [PATCH 01/19] Transaction queue and pool --- .../transactions/transactionState/Pool.java | 39 +++++++++++ .../transactionState/TransactionState.java | 64 +++++++++++++++++++ .../transactionState/ValidTransaction.java | 40 ++++++++++++ .../transactionState/Validity.java | 24 +++++++ .../TransactionStateTest.java | 35 ++++++++++ 5 files changed, 202 insertions(+) create mode 100644 src/main/java/com/limechain/network/protocol/transactions/transactionState/Pool.java create mode 100644 src/main/java/com/limechain/network/protocol/transactions/transactionState/TransactionState.java create mode 100644 src/main/java/com/limechain/network/protocol/transactions/transactionState/ValidTransaction.java create mode 100644 src/main/java/com/limechain/network/protocol/transactions/transactionState/Validity.java create mode 100644 src/test/java/com/limechain/network/protocol/transactions/transactionState/TransactionStateTest.java diff --git a/src/main/java/com/limechain/network/protocol/transactions/transactionState/Pool.java b/src/main/java/com/limechain/network/protocol/transactions/transactionState/Pool.java new file mode 100644 index 000000000..3b8993e9b --- /dev/null +++ b/src/main/java/com/limechain/network/protocol/transactions/transactionState/Pool.java @@ -0,0 +1,39 @@ +package com.limechain.network.protocol.transactions.transactionState; + +import com.limechain.utils.HashUtils; +import lombok.NoArgsConstructor; + +import java.util.HashMap; +import java.util.Map; + +@NoArgsConstructor +public class Pool { + Map transactions = new HashMap<>(); + + public ValidTransaction get(byte[] extrinisics) { + byte[] key = HashUtils.hashWithBlake2b(extrinisics); + return transactions.get(key); + } + + public ValidTransaction[] transactions() { + return transactions.values() + .stream() + .filter(value -> value instanceof ValidTransaction) + .toArray(ValidTransaction[]::new); + } + + public byte[] insert(ValidTransaction validTransaction){ + byte[] key = HashUtils.hashWithBlake2b(validTransaction.getExtrinsic()); + transactions.put(key, validTransaction); + return key; + } + + public void removeExtrinsic(byte[] extrinsic){ + byte[] key = HashUtils.hashWithBlake2b(extrinsic); + transactions.remove(key); + } + + public int length(){ + return transactions.size(); + } +} diff --git a/src/main/java/com/limechain/network/protocol/transactions/transactionState/TransactionState.java b/src/main/java/com/limechain/network/protocol/transactions/transactionState/TransactionState.java new file mode 100644 index 000000000..78d33f5a6 --- /dev/null +++ b/src/main/java/com/limechain/network/protocol/transactions/transactionState/TransactionState.java @@ -0,0 +1,64 @@ +package com.limechain.network.protocol.transactions.transactionState; + +import lombok.Getter; +import lombok.Setter; + +import java.util.PriorityQueue; +import java.util.Queue; + +public class TransactionState { + private static final TransactionState INSTANCE = new TransactionState(); + + public static TransactionState getInstance() { + return INSTANCE; + } + + @Getter + @Setter + private Queue transactionQueue = new PriorityQueue<>(); + + private Pool transactionPool = new Pool(); + + public void pushTransaction(ValidTransaction validTransaction) { + transactionQueue.add(validTransaction); + } + + public ValidTransaction popTransaction() { + return transactionQueue.poll(); + } + + //TODO Pop with timer + + public ValidTransaction peek() { + //return s.queue.Peek() + return transactionQueue.peek(); + } + + public ValidTransaction[] pending() { + return (ValidTransaction[]) transactionQueue.toArray(); + } + + public ValidTransaction[] pendingInPool() { + return transactionPool.transactions(); + } + + public boolean exists(ValidTransaction validTransaction) { + return transactionQueue.contains(validTransaction); + } + + public void removeExtrinsic(byte[] extrinsic) { + transactionPool.removeExtrinsic(extrinsic); + ValidTransaction transactionToBeRemoved = new ValidTransaction(extrinsic); + transactionQueue.remove(transactionToBeRemoved); + } + + public void removeExtrinsicFromPool(byte[] extrinsic) { + transactionPool.removeExtrinsic(extrinsic); + } + + public byte[] addToPool(ValidTransaction validTransaction) { + return transactionPool.insert(validTransaction); + } + + //public void notifyStatus +} diff --git a/src/main/java/com/limechain/network/protocol/transactions/transactionState/ValidTransaction.java b/src/main/java/com/limechain/network/protocol/transactions/transactionState/ValidTransaction.java new file mode 100644 index 000000000..61d8156bf --- /dev/null +++ b/src/main/java/com/limechain/network/protocol/transactions/transactionState/ValidTransaction.java @@ -0,0 +1,40 @@ +package com.limechain.network.protocol.transactions.transactionState; + +import lombok.Getter; + +import java.util.Comparator; + +public class ValidTransaction { + @Getter + private byte[] extrinsic; + @Getter + private Validity validity; + + public ValidTransaction(byte[] extrinsic){ + this.extrinsic = extrinsic; + } + + public ValidTransaction(byte[] extrinsic, Validity validity){ + this.extrinsic = extrinsic; + this.validity = validity; + } + + static class ValidTransactionComparator implements Comparator { + public int compare(ValidTransaction validTransaction, ValidTransaction otherValidTransaction) { + return validTransaction.getValidity().getPriority() + .compareTo(otherValidTransaction.getValidity().getPriority()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ValidTransaction other = (ValidTransaction) o; + + if (!extrinsic.equals(other.getExtrinsic())) return false; + return true; + } + +} diff --git a/src/main/java/com/limechain/network/protocol/transactions/transactionState/Validity.java b/src/main/java/com/limechain/network/protocol/transactions/transactionState/Validity.java new file mode 100644 index 000000000..a4ed555a1 --- /dev/null +++ b/src/main/java/com/limechain/network/protocol/transactions/transactionState/Validity.java @@ -0,0 +1,24 @@ +package com.limechain.network.protocol.transactions.transactionState; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.math.BigInteger; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class Validity { + private BigInteger priority; + private byte[][] requires; + private byte[][] provides; + private BigInteger longevity; + private boolean propagate; + + public Validity(BigInteger priority){ + this.priority = priority; + } +} diff --git a/src/test/java/com/limechain/network/protocol/transactions/transactionState/TransactionStateTest.java b/src/test/java/com/limechain/network/protocol/transactions/transactionState/TransactionStateTest.java new file mode 100644 index 000000000..793743a65 --- /dev/null +++ b/src/test/java/com/limechain/network/protocol/transactions/transactionState/TransactionStateTest.java @@ -0,0 +1,35 @@ +package com.limechain.network.protocol.transactions.transactionState; + +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Comparator; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class TransactionStateTest { + @Test + public void testTransactionState() { + TransactionState transactionState = new TransactionState(); + ValidTransaction[] validTransactions = new ValidTransaction[]{ + new ValidTransaction(new byte[]{'a'}, new Validity(BigInteger.ONE)), + new ValidTransaction(new byte[]{'b'}, new Validity(BigInteger.valueOf(4))), + new ValidTransaction(new byte[]{'c'}, new Validity(BigInteger.valueOf(2))), + new ValidTransaction(new byte[]{'d'}, new Validity(BigInteger.valueOf(17))), + new ValidTransaction(new byte[]{'e'}, new Validity(BigInteger.valueOf(2))), + }; + for (ValidTransaction validTransaction: + validTransactions) { + transactionState.addToPool(validTransaction); + } + + ValidTransaction[] pendingInPool = transactionState.pendingInPool(); + + Arrays.sort(pendingInPool, Comparator.comparing(a -> new String(a.getExtrinsic()))); + + assertArrayEquals(validTransactions, pendingInPool); + assertNull(transactionState.peek()); + } +} From 00d07ab8ce5ac19c4f39feb577bfa5ba34b75034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Thu, 26 Oct 2023 09:23:43 +0300 Subject: [PATCH 02/19] refactor: rename packages to fix checkstyle error --- src/main/java/com/limechain/network/Network.java | 2 +- .../{transactions => transaction}/Transactions.java | 2 +- .../TransactionsController.java | 2 +- .../{transactions => transaction}/TransactionsEngine.java | 6 +++--- .../{transactions => transaction}/TransactionsProtocol.java | 2 +- .../{transactions => transaction}/TransactionsService.java | 2 +- .../scale/TransactionsReader.java | 2 +- .../scale/TransactionsWriter.java | 2 +- .../transactionState => transaction/state}/Pool.java | 2 +- .../state}/TransactionState.java | 2 +- .../state}/ValidTransaction.java | 2 +- .../transactionState => transaction/state}/Validity.java | 2 +- .../state}/TransactionStateTest.java | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) rename src/main/java/com/limechain/network/protocol/{transactions => transaction}/Transactions.java (84%) rename src/main/java/com/limechain/network/protocol/{transactions => transaction}/TransactionsController.java (93%) rename src/main/java/com/limechain/network/protocol/{transactions => transaction}/TransactionsEngine.java (96%) rename src/main/java/com/limechain/network/protocol/{transactions => transaction}/TransactionsProtocol.java (98%) rename src/main/java/com/limechain/network/protocol/{transactions => transaction}/TransactionsService.java (96%) rename src/main/java/com/limechain/network/protocol/{transactions => transaction}/scale/TransactionsReader.java (88%) rename src/main/java/com/limechain/network/protocol/{transactions => transaction}/scale/TransactionsWriter.java (89%) rename src/main/java/com/limechain/network/protocol/{transactions/transactionState => transaction/state}/Pool.java (93%) rename src/main/java/com/limechain/network/protocol/{transactions/transactionState => transaction/state}/TransactionState.java (96%) rename src/main/java/com/limechain/network/protocol/{transactions/transactionState => transaction/state}/ValidTransaction.java (93%) rename src/main/java/com/limechain/network/protocol/{transactions/transactionState => transaction/state}/Validity.java (86%) rename src/test/java/com/limechain/network/protocol/{transactions/transactionState => transaction/state}/TransactionStateTest.java (95%) diff --git a/src/main/java/com/limechain/network/Network.java b/src/main/java/com/limechain/network/Network.java index 01318ff38..336c32c96 100644 --- a/src/main/java/com/limechain/network/Network.java +++ b/src/main/java/com/limechain/network/Network.java @@ -13,7 +13,7 @@ import com.limechain.network.protocol.ping.Ping; import com.limechain.network.protocol.sync.SyncService; import com.limechain.network.protocol.sync.pb.SyncMessage.BlockResponse; -import com.limechain.network.protocol.transactions.TransactionsService; +import com.limechain.network.protocol.transaction.TransactionsService; import com.limechain.network.protocol.warp.WarpSyncService; import com.limechain.network.protocol.warp.dto.WarpSyncResponse; import com.limechain.storage.DBConstants; diff --git a/src/main/java/com/limechain/network/protocol/transactions/Transactions.java b/src/main/java/com/limechain/network/protocol/transaction/Transactions.java similarity index 84% rename from src/main/java/com/limechain/network/protocol/transactions/Transactions.java rename to src/main/java/com/limechain/network/protocol/transaction/Transactions.java index 260f3cc96..7a98b763b 100644 --- a/src/main/java/com/limechain/network/protocol/transactions/Transactions.java +++ b/src/main/java/com/limechain/network/protocol/transaction/Transactions.java @@ -1,4 +1,4 @@ -package com.limechain.network.protocol.transactions; +package com.limechain.network.protocol.transaction; import com.limechain.network.StrictProtocolBinding; diff --git a/src/main/java/com/limechain/network/protocol/transactions/TransactionsController.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsController.java similarity index 93% rename from src/main/java/com/limechain/network/protocol/transactions/TransactionsController.java rename to src/main/java/com/limechain/network/protocol/transaction/TransactionsController.java index 017dc2511..f85fd73f2 100644 --- a/src/main/java/com/limechain/network/protocol/transactions/TransactionsController.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsController.java @@ -1,4 +1,4 @@ -package com.limechain.network.protocol.transactions; +package com.limechain.network.protocol.transaction; import io.libp2p.core.Stream; diff --git a/src/main/java/com/limechain/network/protocol/transactions/TransactionsEngine.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java similarity index 96% rename from src/main/java/com/limechain/network/protocol/transactions/TransactionsEngine.java rename to src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java index a80db5c10..23eda5c09 100644 --- a/src/main/java/com/limechain/network/protocol/transactions/TransactionsEngine.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java @@ -1,8 +1,8 @@ -package com.limechain.network.protocol.transactions; +package com.limechain.network.protocol.transaction; import com.limechain.network.ConnectionManager; -import com.limechain.network.protocol.transactions.scale.TransactionsReader; -import com.limechain.network.protocol.transactions.scale.TransactionsWriter; +import com.limechain.network.protocol.transaction.scale.TransactionsReader; +import com.limechain.network.protocol.transaction.scale.TransactionsWriter; import com.limechain.sync.warpsync.SyncedState; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleCodecWriter; diff --git a/src/main/java/com/limechain/network/protocol/transactions/TransactionsProtocol.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsProtocol.java similarity index 98% rename from src/main/java/com/limechain/network/protocol/transactions/TransactionsProtocol.java rename to src/main/java/com/limechain/network/protocol/transaction/TransactionsProtocol.java index 692fddedf..d8ec8a080 100644 --- a/src/main/java/com/limechain/network/protocol/transactions/TransactionsProtocol.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsProtocol.java @@ -1,4 +1,4 @@ -package com.limechain.network.protocol.transactions; +package com.limechain.network.protocol.transaction; import com.limechain.network.ConnectionManager; import com.limechain.network.encoding.Leb128LengthFrameDecoder; diff --git a/src/main/java/com/limechain/network/protocol/transactions/TransactionsService.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java similarity index 96% rename from src/main/java/com/limechain/network/protocol/transactions/TransactionsService.java rename to src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java index f9123e414..fcd5ed657 100644 --- a/src/main/java/com/limechain/network/protocol/transactions/TransactionsService.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java @@ -1,4 +1,4 @@ -package com.limechain.network.protocol.transactions; +package com.limechain.network.protocol.transaction; import com.limechain.network.ConnectionManager; import com.limechain.network.protocol.NetworkService; diff --git a/src/main/java/com/limechain/network/protocol/transactions/scale/TransactionsReader.java b/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsReader.java similarity index 88% rename from src/main/java/com/limechain/network/protocol/transactions/scale/TransactionsReader.java rename to src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsReader.java index a96da543a..00c99ec05 100644 --- a/src/main/java/com/limechain/network/protocol/transactions/scale/TransactionsReader.java +++ b/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsReader.java @@ -1,4 +1,4 @@ -package com.limechain.network.protocol.transactions.scale; +package com.limechain.network.protocol.transaction.scale; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleReader; diff --git a/src/main/java/com/limechain/network/protocol/transactions/scale/TransactionsWriter.java b/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsWriter.java similarity index 89% rename from src/main/java/com/limechain/network/protocol/transactions/scale/TransactionsWriter.java rename to src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsWriter.java index 6ff270c1c..6460ceedc 100644 --- a/src/main/java/com/limechain/network/protocol/transactions/scale/TransactionsWriter.java +++ b/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsWriter.java @@ -1,4 +1,4 @@ -package com.limechain.network.protocol.transactions.scale; +package com.limechain.network.protocol.transaction.scale; import io.emeraldpay.polkaj.scale.ScaleCodecWriter; import io.emeraldpay.polkaj.scale.ScaleWriter; diff --git a/src/main/java/com/limechain/network/protocol/transactions/transactionState/Pool.java b/src/main/java/com/limechain/network/protocol/transaction/state/Pool.java similarity index 93% rename from src/main/java/com/limechain/network/protocol/transactions/transactionState/Pool.java rename to src/main/java/com/limechain/network/protocol/transaction/state/Pool.java index 3b8993e9b..509ea75af 100644 --- a/src/main/java/com/limechain/network/protocol/transactions/transactionState/Pool.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/Pool.java @@ -1,4 +1,4 @@ -package com.limechain.network.protocol.transactions.transactionState; +package com.limechain.network.protocol.transaction.state; import com.limechain.utils.HashUtils; import lombok.NoArgsConstructor; diff --git a/src/main/java/com/limechain/network/protocol/transactions/transactionState/TransactionState.java b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java similarity index 96% rename from src/main/java/com/limechain/network/protocol/transactions/transactionState/TransactionState.java rename to src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java index 78d33f5a6..fb5c887dd 100644 --- a/src/main/java/com/limechain/network/protocol/transactions/transactionState/TransactionState.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java @@ -1,4 +1,4 @@ -package com.limechain.network.protocol.transactions.transactionState; +package com.limechain.network.protocol.transaction.state; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/limechain/network/protocol/transactions/transactionState/ValidTransaction.java b/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java similarity index 93% rename from src/main/java/com/limechain/network/protocol/transactions/transactionState/ValidTransaction.java rename to src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java index 61d8156bf..7fc5c0396 100644 --- a/src/main/java/com/limechain/network/protocol/transactions/transactionState/ValidTransaction.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java @@ -1,4 +1,4 @@ -package com.limechain.network.protocol.transactions.transactionState; +package com.limechain.network.protocol.transaction.state; import lombok.Getter; diff --git a/src/main/java/com/limechain/network/protocol/transactions/transactionState/Validity.java b/src/main/java/com/limechain/network/protocol/transaction/state/Validity.java similarity index 86% rename from src/main/java/com/limechain/network/protocol/transactions/transactionState/Validity.java rename to src/main/java/com/limechain/network/protocol/transaction/state/Validity.java index a4ed555a1..35472343b 100644 --- a/src/main/java/com/limechain/network/protocol/transactions/transactionState/Validity.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/Validity.java @@ -1,4 +1,4 @@ -package com.limechain.network.protocol.transactions.transactionState; +package com.limechain.network.protocol.transaction.state; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/test/java/com/limechain/network/protocol/transactions/transactionState/TransactionStateTest.java b/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java similarity index 95% rename from src/test/java/com/limechain/network/protocol/transactions/transactionState/TransactionStateTest.java rename to src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java index 793743a65..a8ba75d37 100644 --- a/src/test/java/com/limechain/network/protocol/transactions/transactionState/TransactionStateTest.java +++ b/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java @@ -1,4 +1,4 @@ -package com.limechain.network.protocol.transactions.transactionState; +package com.limechain.network.protocol.transaction.state; import org.junit.jupiter.api.Test; From 00d07ab4e1ab30802e8e6d5a5fb6ac1ea8c22943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Thu, 26 Oct 2023 15:13:50 +0300 Subject: [PATCH 03/19] feat: pop transaction with timer --- .../protocol/transaction/state/Pool.java | 8 ++- .../transaction/state/TransactionState.java | 54 ++++++++++++++++--- .../transaction/state/ValidTransaction.java | 6 ++- .../java/com/limechain/utils/TimeOutTask.java | 22 ++++++++ 4 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/limechain/utils/TimeOutTask.java diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/Pool.java b/src/main/java/com/limechain/network/protocol/transaction/state/Pool.java index 509ea75af..b2b107ecd 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/Pool.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/Pool.java @@ -8,18 +8,16 @@ @NoArgsConstructor public class Pool { - Map transactions = new HashMap<>(); + final Map transactions = new HashMap<>(); public ValidTransaction get(byte[] extrinisics) { byte[] key = HashUtils.hashWithBlake2b(extrinisics); + return transactions.get(key); } public ValidTransaction[] transactions() { - return transactions.values() - .stream() - .filter(value -> value instanceof ValidTransaction) - .toArray(ValidTransaction[]::new); + return transactions.values().toArray(ValidTransaction[]::new); } public byte[] insert(ValidTransaction validTransaction){ diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java index fb5c887dd..fd1ad2176 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java @@ -1,23 +1,30 @@ package com.limechain.network.protocol.transaction.state; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; +import org.jetbrains.annotations.NotNull; import java.util.PriorityQueue; import java.util.Queue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeoutException; +@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) public class TransactionState { private static final TransactionState INSTANCE = new TransactionState(); - - public static TransactionState getInstance() { - return INSTANCE; - } - + private final Pool transactionPool = new Pool(); @Getter @Setter private Queue transactionQueue = new PriorityQueue<>(); + private final ExecutorService executor = Executors.newSingleThreadExecutor(); - private Pool transactionPool = new Pool(); + public static TransactionState getInstance() { + return INSTANCE; + } public void pushTransaction(ValidTransaction validTransaction) { transactionQueue.add(validTransaction); @@ -27,7 +34,38 @@ public ValidTransaction popTransaction() { return transactionQueue.poll(); } - //TODO Pop with timer + public ValidTransaction popTransactionWithTimer(long timeout) { + ValidTransaction validTransaction = popTransaction(); + if (validTransaction != null) return validTransaction; + + Future futureTransaction = popFutureTransaction(); + + try { + return futureTransaction.get(timeout, java.util.concurrent.TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } catch (TimeoutException e) { + futureTransaction.cancel(true); + } + + return null; + } + + @NotNull + private Future popFutureTransaction() { + return executor.submit(() -> { + ValidTransaction transaction = null; + while (transaction == null) { + transaction = popTransaction(); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + return null; + } + } + return transaction; + }); + } public ValidTransaction peek() { //return s.queue.Peek() @@ -35,7 +73,7 @@ public ValidTransaction peek() { } public ValidTransaction[] pending() { - return (ValidTransaction[]) transactionQueue.toArray(); + return transactionQueue.toArray(new ValidTransaction[0]); } public ValidTransaction[] pendingInPool() { diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java b/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java index 7fc5c0396..509e0c449 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java @@ -4,7 +4,7 @@ import java.util.Comparator; -public class ValidTransaction { +public class ValidTransaction implements Comparable { @Getter private byte[] extrinsic; @Getter @@ -19,6 +19,10 @@ public ValidTransaction(byte[] extrinsic, Validity validity){ this.validity = validity; } + public int compareTo(ValidTransaction transaction) { + return new ValidTransactionComparator().compare(this, transaction); + } + static class ValidTransactionComparator implements Comparator { public int compare(ValidTransaction validTransaction, ValidTransaction otherValidTransaction) { return validTransaction.getValidity().getPriority() diff --git a/src/main/java/com/limechain/utils/TimeOutTask.java b/src/main/java/com/limechain/utils/TimeOutTask.java new file mode 100644 index 000000000..29ee80168 --- /dev/null +++ b/src/main/java/com/limechain/utils/TimeOutTask.java @@ -0,0 +1,22 @@ +package com.limechain.utils; + +import java.util.Timer; +import java.util.TimerTask; + +class TimeOutTask extends TimerTask { + private Thread thread; + private Timer timer; + + public TimeOutTask(Thread thread, Timer timer) { + this.thread = thread; + this.timer = timer; + } + + @Override + public void run() { + if(thread != null && thread.isAlive()) { + thread.interrupt(); + timer.cancel(); + } + } +} \ No newline at end of file From 00d07ab01b1e852eaa51afee85a8093346ea1bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Thu, 26 Oct 2023 15:15:50 +0300 Subject: [PATCH 04/19] fix: build, change constructor access --- .../network/protocol/transaction/state/TransactionState.java | 4 ++-- .../protocol/transaction/state/TransactionStateTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java index fd1ad2176..3f78d299c 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java @@ -1,5 +1,6 @@ package com.limechain.network.protocol.transaction.state; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -13,7 +14,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; -@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class TransactionState { private static final TransactionState INSTANCE = new TransactionState(); private final Pool transactionPool = new Pool(); @@ -68,7 +69,6 @@ private Future popFutureTransaction() { } public ValidTransaction peek() { - //return s.queue.Peek() return transactionQueue.peek(); } diff --git a/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java b/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java index a8ba75d37..2d61b188c 100644 --- a/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java +++ b/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java @@ -9,9 +9,9 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertNull; -public class TransactionStateTest { +class TransactionStateTest { @Test - public void testTransactionState() { + void testTransactionState() { TransactionState transactionState = new TransactionState(); ValidTransaction[] validTransactions = new ValidTransaction[]{ new ValidTransaction(new byte[]{'a'}, new Validity(BigInteger.ONE)), From 00d07abd34289f9497e7679060ab40281c445f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Thu, 26 Oct 2023 15:41:47 +0300 Subject: [PATCH 05/19] refactor: replace equals with lombok --- .../transaction/state/ValidTransaction.java | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java b/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java index 509e0c449..3c3265ffc 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java @@ -1,12 +1,16 @@ package com.limechain.network.protocol.transaction.state; +import lombok.EqualsAndHashCode; import lombok.Getter; +import org.jetbrains.annotations.NotNull; import java.util.Comparator; +@EqualsAndHashCode(onlyExplicitlyIncluded = true) public class ValidTransaction implements Comparable { @Getter - private byte[] extrinsic; + @EqualsAndHashCode.Include + private final byte[] extrinsic; @Getter private Validity validity; @@ -19,7 +23,7 @@ public ValidTransaction(byte[] extrinsic, Validity validity){ this.validity = validity; } - public int compareTo(ValidTransaction transaction) { + public int compareTo(@NotNull ValidTransaction transaction) { return new ValidTransactionComparator().compare(this, transaction); } @@ -30,15 +34,4 @@ public int compare(ValidTransaction validTransaction, ValidTransaction otherVali } } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ValidTransaction other = (ValidTransaction) o; - - if (!extrinsic.equals(other.getExtrinsic())) return false; - return true; - } - } From 00d07ab38e544c30a6fcada64015bfaf1c5853d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Thu, 26 Oct 2023 15:43:29 +0300 Subject: [PATCH 06/19] chore: delete obsolete file --- .../java/com/limechain/utils/TimeOutTask.java | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 src/main/java/com/limechain/utils/TimeOutTask.java diff --git a/src/main/java/com/limechain/utils/TimeOutTask.java b/src/main/java/com/limechain/utils/TimeOutTask.java deleted file mode 100644 index 29ee80168..000000000 --- a/src/main/java/com/limechain/utils/TimeOutTask.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.limechain.utils; - -import java.util.Timer; -import java.util.TimerTask; - -class TimeOutTask extends TimerTask { - private Thread thread; - private Timer timer; - - public TimeOutTask(Thread thread, Timer timer) { - this.thread = thread; - this.timer = timer; - } - - @Override - public void run() { - if(thread != null && thread.isAlive()) { - thread.interrupt(); - timer.cancel(); - } - } -} \ No newline at end of file From 00d07abf3a3e780cee536f223b94d5a0f06e6f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Wed, 22 Nov 2023 15:15:50 +0200 Subject: [PATCH 07/19] chore: prepare for transaction validaiton --- .../transaction/TransactionsEngine.java | 25 ++++++++++++++++--- .../transaction/scale/TransactionsReader.java | 10 +++++--- .../transaction/scale/TransactionsWriter.java | 7 +++--- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java index eb7ac6e66..517595aad 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java @@ -3,7 +3,11 @@ import com.limechain.network.ConnectionManager; import com.limechain.network.protocol.transaction.scale.TransactionsReader; import com.limechain.network.protocol.transaction.scale.TransactionsWriter; +import com.limechain.network.protocol.warp.dto.BlockHeader; +import com.limechain.network.protocol.warp.dto.Extrinsics; import com.limechain.network.protocol.warp.exception.ScaleEncodingException; +import com.limechain.runtime.Runtime; +import com.limechain.storage.block.BlockState; import com.limechain.sync.warpsync.SyncedState; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleCodecWriter; @@ -98,10 +102,23 @@ private void handleHandshake(PeerId peerId, Stream stream) { private void handleTransactionMessage(byte[] message, PeerId peerId) { ScaleCodecReader reader = new ScaleCodecReader(message); - byte[][] transactions = reader.read(new TransactionsReader()); + Extrinsics[] transactions = reader.read(new TransactionsReader()); log.log(Level.INFO, "Received " + transactions.length + " transactions from Peer " + peerId); - //TODO Add transactions to data + + final BlockHeader header = BlockState.getInstance().bestBlockHeader(); + if (header == null) { + log.log(Level.WARNING, "No best block header found"); + return; + } + + final Runtime runtime = BlockState.getInstance().getRuntime(header.getHash()); + if (runtime == null) { + log.log(Level.WARNING, "No runtime found for block header " + header.getHash()); + return; + } + //TODO Validate transaction using runtime and then add to transaction pool + // (depends on StateStorage and TrieState) } /** @@ -126,7 +143,9 @@ public void writeHandshakeToStream(Stream stream, PeerId peerId) { public void writeTransactionsMessage(Stream stream, PeerId peerId) { ByteArrayOutputStream buf = new ByteArrayOutputStream(); try (ScaleCodecWriter writer = new ScaleCodecWriter(buf)) { - writer.write(new TransactionsWriter(), new byte[][]{new byte[]{}, new byte[]{}}); + writer.write(new TransactionsWriter(), new Extrinsics[]{ + new Extrinsics(new byte[]{}), new Extrinsics(new byte[]{}) + }); } catch (IOException e) { throw new ScaleEncodingException(e); } diff --git a/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsReader.java b/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsReader.java index 00c99ec05..c614b09b2 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsReader.java +++ b/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsReader.java @@ -1,15 +1,17 @@ package com.limechain.network.protocol.transaction.scale; +import com.limechain.network.protocol.warp.dto.Extrinsics; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleReader; -public class TransactionsReader implements ScaleReader { +public class TransactionsReader implements ScaleReader { + @Override - public byte[][] read(ScaleCodecReader reader) { + public Extrinsics[] read(ScaleCodecReader reader) { int size = reader.readCompactInt(); - byte[][] transactions = new byte[size][]; + Extrinsics[] transactions = new Extrinsics[size]; for (int i = 0; i < size; i++) { - transactions[i] = reader.readByteArray(); + transactions[i] = new Extrinsics(reader.readByteArray()); } return transactions; } diff --git a/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsWriter.java b/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsWriter.java index 49265f1fb..ddcb21a97 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsWriter.java +++ b/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsWriter.java @@ -1,17 +1,18 @@ package com.limechain.network.protocol.transaction.scale; +import com.limechain.network.protocol.warp.dto.Extrinsics; import io.emeraldpay.polkaj.scale.ScaleCodecWriter; import io.emeraldpay.polkaj.scale.ScaleWriter; import java.io.IOException; -public class TransactionsWriter implements ScaleWriter { +public class TransactionsWriter implements ScaleWriter { @Override - public void write(ScaleCodecWriter writer, byte[][] transactions) throws IOException { + public void write(ScaleCodecWriter writer, Extrinsics[] transactions) throws IOException { writer.writeCompact(transactions.length); for (int i = 0; i < transactions.length; i++) { - writer.writeAsList(transactions[i]); + writer.writeAsList(transactions[i].getExtrinsic()); } } } From 00d07ab648df35a2225b9ca60f1c83893e647848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Wed, 22 Nov 2023 15:24:25 +0200 Subject: [PATCH 08/19] chore: remove stacktrace --- .../network/protocol/transaction/TransactionsProtocol.java | 1 - .../network/protocol/transaction/state/TransactionState.java | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/limechain/network/protocol/transaction/TransactionsProtocol.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsProtocol.java index d8ec8a080..0c58bf6e6 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/TransactionsProtocol.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsProtocol.java @@ -93,7 +93,6 @@ public void onException(Throwable cause) { connectionManager.closeTransactionsStream(stream); if (cause != null) { log.log(Level.WARNING, "Transactions Exception: " + cause.getMessage()); - cause.printStackTrace(); } else { log.log(Level.WARNING, "Transactions Exception with unknown cause"); } diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java index 3f78d299c..5c7f15cf2 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import lombok.extern.java.Log; import org.jetbrains.annotations.NotNull; import java.util.PriorityQueue; @@ -15,13 +16,14 @@ import java.util.concurrent.TimeoutException; @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Log public class TransactionState { private static final TransactionState INSTANCE = new TransactionState(); private final Pool transactionPool = new Pool(); + private final ExecutorService executor = Executors.newSingleThreadExecutor(); @Getter @Setter private Queue transactionQueue = new PriorityQueue<>(); - private final ExecutorService executor = Executors.newSingleThreadExecutor(); public static TransactionState getInstance() { return INSTANCE; @@ -44,6 +46,7 @@ public ValidTransaction popTransactionWithTimer(long timeout) { try { return futureTransaction.get(timeout, java.util.concurrent.TimeUnit.MILLISECONDS); } catch (InterruptedException | ExecutionException e) { + log.severe("Error while waiting for transaction: " + e.getMessage()); e.printStackTrace(); } catch (TimeoutException e) { futureTransaction.cancel(true); From 00d07abf8ef71221ac582a843066c545f4b8bc8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Wed, 22 Nov 2023 15:25:05 +0200 Subject: [PATCH 09/19] chore: remove redundant return --- .../network/protocol/transaction/TransactionsEngine.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java index 517595aad..2dca11c38 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java @@ -115,7 +115,6 @@ private void handleTransactionMessage(byte[] message, PeerId peerId) { final Runtime runtime = BlockState.getInstance().getRuntime(header.getHash()); if (runtime == null) { log.log(Level.WARNING, "No runtime found for block header " + header.getHash()); - return; } //TODO Validate transaction using runtime and then add to transaction pool // (depends on StateStorage and TrieState) From 00d07ab697de11868c8c34db5150b1ebb19134f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Wed, 22 Nov 2023 15:26:02 +0200 Subject: [PATCH 10/19] chore: remove stacktrace --- .../network/protocol/transaction/state/TransactionState.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java index 5c7f15cf2..c3a31c4af 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java @@ -47,7 +47,6 @@ public ValidTransaction popTransactionWithTimer(long timeout) { return futureTransaction.get(timeout, java.util.concurrent.TimeUnit.MILLISECONDS); } catch (InterruptedException | ExecutionException e) { log.severe("Error while waiting for transaction: " + e.getMessage()); - e.printStackTrace(); } catch (TimeoutException e) { futureTransaction.cancel(true); } From 00d07ab33a14a54b2a0f9a1c5e76ba1eedf21219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Wed, 22 Nov 2023 15:30:01 +0200 Subject: [PATCH 11/19] rethrow interrupted exception --- .../transaction/state/TransactionState.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java index c3a31c4af..e9df9aa05 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java @@ -37,7 +37,7 @@ public ValidTransaction popTransaction() { return transactionQueue.poll(); } - public ValidTransaction popTransactionWithTimer(long timeout) { + public ValidTransaction popTransactionWithTimer(long timeout) throws InterruptedException { ValidTransaction validTransaction = popTransaction(); if (validTransaction != null) return validTransaction; @@ -45,10 +45,13 @@ public ValidTransaction popTransactionWithTimer(long timeout) { try { return futureTransaction.get(timeout, java.util.concurrent.TimeUnit.MILLISECONDS); - } catch (InterruptedException | ExecutionException e) { + } catch (ExecutionException e) { log.severe("Error while waiting for transaction: " + e.getMessage()); } catch (TimeoutException e) { futureTransaction.cancel(true); + } catch (InterruptedException e) { + if (Thread.interrupted()) + throw new InterruptedException(); } return null; @@ -60,11 +63,7 @@ private Future popFutureTransaction() { ValidTransaction transaction = null; while (transaction == null) { transaction = popTransaction(); - try { - Thread.sleep(50); - } catch (InterruptedException e) { - return null; - } + Thread.sleep(50); } return transaction; }); From 00d07ab4a702894b14d8204aa0f4178f3c37e3e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Wed, 22 Nov 2023 16:14:00 +0200 Subject: [PATCH 12/19] refactor: limit constructor access --- .../network/protocol/transaction/state/TransactionState.java | 2 +- .../protocol/transaction/state/TransactionStateTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java index e9df9aa05..bd2d3a162 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java @@ -15,8 +15,8 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; -@NoArgsConstructor(access = AccessLevel.PROTECTED) @Log +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class TransactionState { private static final TransactionState INSTANCE = new TransactionState(); private final Pool transactionPool = new Pool(); diff --git a/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java b/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java index 2d61b188c..33aec44a0 100644 --- a/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java +++ b/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java @@ -12,7 +12,7 @@ class TransactionStateTest { @Test void testTransactionState() { - TransactionState transactionState = new TransactionState(); + TransactionState transactionState = TransactionState.getInstance(); ValidTransaction[] validTransactions = new ValidTransaction[]{ new ValidTransaction(new byte[]{'a'}, new Validity(BigInteger.ONE)), new ValidTransaction(new byte[]{'b'}, new Validity(BigInteger.valueOf(4))), From 00d07ab113b416bc6499cfc7a38bb162eb1894ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Wed, 22 Nov 2023 17:08:58 +0200 Subject: [PATCH 13/19] chore: remove redundant comparator --- .../protocol/transaction/state/TransactionState.java | 2 +- .../protocol/transaction/state/ValidTransaction.java | 12 ++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java index bd2d3a162..98a2c6176 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java @@ -99,5 +99,5 @@ public byte[] addToPool(ValidTransaction validTransaction) { return transactionPool.insert(validTransaction); } - //public void notifyStatus + //Todo: notifyStatus ? } diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java b/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java index 3c3265ffc..c5fd9f1af 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java @@ -4,8 +4,6 @@ import lombok.Getter; import org.jetbrains.annotations.NotNull; -import java.util.Comparator; - @EqualsAndHashCode(onlyExplicitlyIncluded = true) public class ValidTransaction implements Comparable { @Getter @@ -24,14 +22,8 @@ public ValidTransaction(byte[] extrinsic, Validity validity){ } public int compareTo(@NotNull ValidTransaction transaction) { - return new ValidTransactionComparator().compare(this, transaction); - } - - static class ValidTransactionComparator implements Comparator { - public int compare(ValidTransaction validTransaction, ValidTransaction otherValidTransaction) { - return validTransaction.getValidity().getPriority() - .compareTo(otherValidTransaction.getValidity().getPriority()); - } + return this.getValidity().getPriority() + .compareTo(transaction.getValidity().getPriority()); } } From 00d07ab1941ccb59aea5a7931f6530bd4e0fb1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Wed, 29 Nov 2023 10:05:51 +0200 Subject: [PATCH 14/19] chore: fix imports --- .../network/protocol/transaction/TransactionsEngine.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java index 857a4144e..5226bc327 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java @@ -1,14 +1,14 @@ package com.limechain.network.protocol.transaction; import com.limechain.network.ConnectionManager; -import com.limechain.network.protocol.transactions.scale.TransactionsReader; -import com.limechain.network.protocol.transactions.scale.TransactionsWriter; +import com.limechain.network.protocol.transaction.scale.TransactionsReader; +import com.limechain.network.protocol.transaction.scale.TransactionsWriter; import com.limechain.network.protocol.warp.dto.BlockHeader; import com.limechain.network.protocol.warp.dto.Extrinsics; -import com.limechain.network.protocol.warp.exception.ScaleEncodingException; import com.limechain.runtime.Runtime; import com.limechain.storage.block.BlockState; import com.limechain.sync.warpsync.SyncedState; +import com.limechain.utils.scale.exceptions.ScaleEncodingException; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleCodecWriter; import io.libp2p.core.PeerId; From 00d07ab9b63d8d5698edea380e45cc70ed7ba93d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9Cur=D0=B0d=20H=D0=B0mz=D0=B0?= Date: Wed, 29 Nov 2023 10:08:44 +0200 Subject: [PATCH 15/19] chore: reformat --- .../network/protocol/transaction/state/ValidTransaction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java b/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java index c5fd9f1af..f91017f9e 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java +++ b/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java @@ -12,11 +12,11 @@ public class ValidTransaction implements Comparable { @Getter private Validity validity; - public ValidTransaction(byte[] extrinsic){ + public ValidTransaction(byte[] extrinsic) { this.extrinsic = extrinsic; } - public ValidTransaction(byte[] extrinsic, Validity validity){ + public ValidTransaction(byte[] extrinsic, Validity validity) { this.extrinsic = extrinsic; this.validity = validity; } From 98fb51765b8b32a2cc01a23ac04eba556026fc49 Mon Sep 17 00:00:00 2001 From: Yordan Atanasov Date: Mon, 14 Oct 2024 19:36:09 +0300 Subject: [PATCH 16/19] feat: Implement transaction validation. --- .../com/limechain/chain/spec/Genesis.java | 2 - .../misc/RuntimeApiVersionException.java | 7 +++ .../network/kad/KademliaService.java | 2 +- .../blockannounce/BlockAnnounceProtocol.java | 3 +- .../transaction/TransactionsEngine.java | 24 ++++---- .../transaction/TransactionsService.java | 3 +- .../transaction/scale/TransactionsReader.java | 13 ++-- .../transaction/scale/TransactionsWriter.java | 13 ++-- .../transaction/state/ValidTransaction.java | 29 --------- .../protocol/transaction/state/Validity.java | 24 -------- .../protocol/warp/WarpSyncProtocol.java | 2 +- .../network/protocol/warp/dto/BlockBody.java | 5 +- .../warp/scale/reader/BlockBodyReader.java | 6 +- .../scale/reader/JustificationReader.java | 2 +- .../scale/reader/WarpSyncFragmentReader.java | 2 - .../warp/scale/writer/BlockBodyWriter.java | 8 +-- .../rpc/methods/chain/ChainRPCImpl.java | 6 +- .../java/com/limechain/rpc/server/RpcApp.java | 11 ++-- .../rpc/server/UnsafeInterceptor.java | 2 +- .../chainhead/ChainHeadRpcImpl.java | 4 +- .../transaction/TransactionRpcImpl.java | 4 +- .../java/com/limechain/runtime/Context.java | 2 +- .../java/com/limechain/runtime/Runtime.java | 14 +++-- .../limechain/runtime/RuntimeEndpoint.java | 2 + .../limechain/runtime/allocator/Header.java | 2 +- .../runtime/version/ApiVersionName.java | 21 +++++++ .../version/scale/RuntimeVersionReader.java | 2 +- .../storage/block/BlockStateHelper.java | 2 +- .../sync/fullsync/FullSyncMachine.java | 54 ++++++++-------- .../action/ChainInformationBuildAction.java | 22 +++---- .../TransactionPool.java} | 20 +++--- .../TransactionState.java | 10 +-- .../transaction/TransactionValidator.java | 61 +++++++++++++++++++ .../dto/Extrinsic.java} | 10 +-- .../transaction/dto/ExtrinsicArray.java | 11 ++++ .../dto/InvalidTransactionType.java | 40 ++++++++++++ .../transaction/dto/TransactionSource.java | 33 ++++++++++ .../dto/TransactionValidationRequest.java | 17 ++++++ .../dto/TransactionValidationResponse.java | 14 +++++ .../transaction/dto/TransactionValidity.java | 15 +++++ .../dto/TransactionValidityError.java | 6 ++ .../dto/UnknownTransactionType.java | 33 ++++++++++ .../transaction/dto/ValidTransaction.java | 15 +++++ .../java/com/limechain/trie/decoded/Node.java | 2 +- .../limechain/trie/dto/node/DecodedNode.java | 2 +- .../utils/scale/readers/PairReader.java | 2 +- .../readers/TransactionValidationReader.java | 59 ++++++++++++++++++ .../utils/scale/writers/IterableWriter.java | 1 - .../writers/TransactionValidationWriter.java | 24 ++++++++ .../BlockAnnounceControllerTest.java | 2 - .../state/TransactionStateTest.java | 21 ++++--- .../hostapi/OffchainHostFunctionsTest.java | 6 +- .../limechain/storage/DBInitializerTest.java | 1 - .../sync/fullsync/BlockExecutorTest.java | 4 +- .../trie/structure/slab/SlabTest.java | 4 +- 55 files changed, 505 insertions(+), 201 deletions(-) create mode 100644 src/main/java/com/limechain/exception/misc/RuntimeApiVersionException.java delete mode 100644 src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java delete mode 100644 src/main/java/com/limechain/network/protocol/transaction/state/Validity.java create mode 100644 src/main/java/com/limechain/runtime/version/ApiVersionName.java rename src/main/java/com/limechain/{network/protocol/transaction/state/Pool.java => transaction/TransactionPool.java} (60%) rename src/main/java/com/limechain/{network/protocol/transaction/state => transaction}/TransactionState.java (92%) create mode 100644 src/main/java/com/limechain/transaction/TransactionValidator.java rename src/main/java/com/limechain/{network/protocol/warp/dto/Extrinsics.java => transaction/dto/Extrinsic.java} (77%) create mode 100644 src/main/java/com/limechain/transaction/dto/ExtrinsicArray.java create mode 100644 src/main/java/com/limechain/transaction/dto/InvalidTransactionType.java create mode 100644 src/main/java/com/limechain/transaction/dto/TransactionSource.java create mode 100644 src/main/java/com/limechain/transaction/dto/TransactionValidationRequest.java create mode 100644 src/main/java/com/limechain/transaction/dto/TransactionValidationResponse.java create mode 100644 src/main/java/com/limechain/transaction/dto/TransactionValidity.java create mode 100644 src/main/java/com/limechain/transaction/dto/TransactionValidityError.java create mode 100644 src/main/java/com/limechain/transaction/dto/UnknownTransactionType.java create mode 100644 src/main/java/com/limechain/transaction/dto/ValidTransaction.java create mode 100644 src/main/java/com/limechain/utils/scale/readers/TransactionValidationReader.java create mode 100644 src/main/java/com/limechain/utils/scale/writers/TransactionValidationWriter.java diff --git a/src/main/java/com/limechain/chain/spec/Genesis.java b/src/main/java/com/limechain/chain/spec/Genesis.java index 19b00effb..2793be7c6 100644 --- a/src/main/java/com/limechain/chain/spec/Genesis.java +++ b/src/main/java/com/limechain/chain/spec/Genesis.java @@ -5,12 +5,10 @@ import com.fasterxml.jackson.annotation.JsonSetter; import com.google.protobuf.ByteString; import com.limechain.utils.StringUtils; -import lombok.Getter; import org.apache.tomcat.util.buf.HexUtils; import java.io.Serializable; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; diff --git a/src/main/java/com/limechain/exception/misc/RuntimeApiVersionException.java b/src/main/java/com/limechain/exception/misc/RuntimeApiVersionException.java new file mode 100644 index 000000000..8b67ac7c7 --- /dev/null +++ b/src/main/java/com/limechain/exception/misc/RuntimeApiVersionException.java @@ -0,0 +1,7 @@ +package com.limechain.exception.misc; + +public class RuntimeApiVersionException extends RuntimeException { + public RuntimeApiVersionException(String message) { + super(message); + } +} diff --git a/src/main/java/com/limechain/network/kad/KademliaService.java b/src/main/java/com/limechain/network/kad/KademliaService.java index a848efd3e..bf80746bd 100644 --- a/src/main/java/com/limechain/network/kad/KademliaService.java +++ b/src/main/java/com/limechain/network/kad/KademliaService.java @@ -18,8 +18,8 @@ import org.peergos.protocol.dht.RamRecordStore; import java.util.ArrayList; -import java.util.List; import java.util.Arrays; +import java.util.List; import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; diff --git a/src/main/java/com/limechain/network/protocol/blockannounce/BlockAnnounceProtocol.java b/src/main/java/com/limechain/network/protocol/blockannounce/BlockAnnounceProtocol.java index 49e1e6adb..8ee7dc153 100644 --- a/src/main/java/com/limechain/network/protocol/blockannounce/BlockAnnounceProtocol.java +++ b/src/main/java/com/limechain/network/protocol/blockannounce/BlockAnnounceProtocol.java @@ -11,9 +11,8 @@ import lombok.extern.java.Log; import org.jetbrains.annotations.NotNull; -import java.util.logging.Level; - import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; @Log public class BlockAnnounceProtocol extends ProtocolHandler { diff --git a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java index 1518653c8..6d1702b6f 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java @@ -5,14 +5,11 @@ import com.limechain.network.protocol.transaction.scale.TransactionsReader; import com.limechain.network.protocol.transaction.scale.TransactionsWriter; import com.limechain.network.protocol.warp.dto.BlockHeader; -import com.limechain.network.protocol.warp.dto.Extrinsics; import com.limechain.runtime.Runtime; import com.limechain.storage.block.BlockState; -import com.limechain.sync.warpsync.SyncedState; -import com.limechain.utils.scale.exceptions.ScaleEncodingException; -import com.limechain.network.protocol.transactions.scale.TransactionsReader; -import com.limechain.network.protocol.transactions.scale.TransactionsWriter; import com.limechain.sync.warpsync.WarpSyncState; +import com.limechain.transaction.dto.Extrinsic; +import com.limechain.transaction.dto.ExtrinsicArray; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleCodecWriter; import io.libp2p.core.PeerId; @@ -72,7 +69,7 @@ private void handleInitiatorStreamMessage(byte[] message, Stream stream) { } connectionManager.addTransactionsStream(stream); log.log(Level.INFO, "Received transactions handshake from " + peerId); - //TODO Send valid transactions to the peer we received a handshake from + //TODO *2nd* Send valid transactions to the peer we received a handshake from } private void handleResponderStreamMessage(byte[] message, Stream stream) { @@ -105,8 +102,8 @@ private void handleHandshake(PeerId peerId, Stream stream) { private void handleTransactionMessage(byte[] message, PeerId peerId) { ScaleCodecReader reader = new ScaleCodecReader(message); - Extrinsics[] transactions = reader.read(new TransactionsReader()); - log.log(Level.INFO, "Received " + transactions.length + " transactions from Peer " + ExtrinsicArray transactions = reader.read(new TransactionsReader()); + log.log(Level.INFO, "Received " + transactions.getExtrinsics().length + " transactions from Peer " + peerId); final BlockHeader header = BlockState.getInstance().bestBlockHeader(); @@ -119,7 +116,7 @@ private void handleTransactionMessage(byte[] message, PeerId peerId) { if (runtime == null) { log.log(Level.WARNING, "No runtime found for block header " + header.getHash()); } - //TODO Validate transaction using runtime and then add to transaction pool + //TODO *1st* Validate transaction using runtime and then add to transaction pool // (depends on StateStorage and TrieState) } @@ -145,15 +142,16 @@ public void writeHandshakeToStream(Stream stream, PeerId peerId) { public void writeTransactionsMessage(Stream stream, PeerId peerId) { ByteArrayOutputStream buf = new ByteArrayOutputStream(); try (ScaleCodecWriter writer = new ScaleCodecWriter(buf)) { - writer.write(new TransactionsWriter(), new Extrinsics[]{ - new Extrinsics(new byte[]{}), new Extrinsics(new byte[]{}) - }); + writer.write(new TransactionsWriter(), new ExtrinsicArray(new Extrinsic[]{ + new Extrinsic(new byte[]{}), new Extrinsic(new byte[]{}) + })); } catch (IOException e) { throw new ScaleEncodingException(e); } log.log(Level.INFO, "Sending transaction message to peer " + peerId); - //TODO Send our transaction message + //TODO *3rd* Send our transaction message + // (this is probably used after an extrinsic is received via an RPC call or created internally). } private boolean isHandshake(byte[] message) { diff --git a/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java index ca42b38c8..630a2dfed 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java @@ -34,7 +34,8 @@ public void sendTransactionsMessage(Host us, PeerId peerId) { } private void sendTransactions(Stream stream) { - //TODO Send transaction messages + //TODO *Last* Send transaction messages + // (scheduled task to notify peers about transactions in the TP and TQ). } private void sendHandshake(Host us, PeerId peerId) { diff --git a/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsReader.java b/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsReader.java index c614b09b2..26696b152 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsReader.java +++ b/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsReader.java @@ -1,18 +1,19 @@ package com.limechain.network.protocol.transaction.scale; -import com.limechain.network.protocol.warp.dto.Extrinsics; +import com.limechain.transaction.dto.Extrinsic; +import com.limechain.transaction.dto.ExtrinsicArray; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleReader; -public class TransactionsReader implements ScaleReader { +public class TransactionsReader implements ScaleReader { @Override - public Extrinsics[] read(ScaleCodecReader reader) { + public ExtrinsicArray read(ScaleCodecReader reader) { int size = reader.readCompactInt(); - Extrinsics[] transactions = new Extrinsics[size]; + Extrinsic[] transactions = new Extrinsic[size]; for (int i = 0; i < size; i++) { - transactions[i] = new Extrinsics(reader.readByteArray()); + transactions[i] = new Extrinsic(reader.readByteArray()); } - return transactions; + return new ExtrinsicArray(transactions); } } diff --git a/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsWriter.java b/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsWriter.java index ddcb21a97..d9f1f4255 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsWriter.java +++ b/src/main/java/com/limechain/network/protocol/transaction/scale/TransactionsWriter.java @@ -1,18 +1,19 @@ package com.limechain.network.protocol.transaction.scale; -import com.limechain.network.protocol.warp.dto.Extrinsics; +import com.limechain.transaction.dto.ExtrinsicArray; import io.emeraldpay.polkaj.scale.ScaleCodecWriter; import io.emeraldpay.polkaj.scale.ScaleWriter; import java.io.IOException; -public class TransactionsWriter implements ScaleWriter { +public class TransactionsWriter implements ScaleWriter { @Override - public void write(ScaleCodecWriter writer, Extrinsics[] transactions) throws IOException { - writer.writeCompact(transactions.length); - for (int i = 0; i < transactions.length; i++) { - writer.writeAsList(transactions[i].getExtrinsic()); + public void write(ScaleCodecWriter writer, ExtrinsicArray holder) throws IOException { + int length = holder.getExtrinsics().length; + writer.writeCompact(length); + for (int i = 0; i < length; i++) { + writer.writeAsList(holder.getExtrinsics()[i].getData()); } } } diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java b/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java deleted file mode 100644 index f91017f9e..000000000 --- a/src/main/java/com/limechain/network/protocol/transaction/state/ValidTransaction.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.limechain.network.protocol.transaction.state; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import org.jetbrains.annotations.NotNull; - -@EqualsAndHashCode(onlyExplicitlyIncluded = true) -public class ValidTransaction implements Comparable { - @Getter - @EqualsAndHashCode.Include - private final byte[] extrinsic; - @Getter - private Validity validity; - - public ValidTransaction(byte[] extrinsic) { - this.extrinsic = extrinsic; - } - - public ValidTransaction(byte[] extrinsic, Validity validity) { - this.extrinsic = extrinsic; - this.validity = validity; - } - - public int compareTo(@NotNull ValidTransaction transaction) { - return this.getValidity().getPriority() - .compareTo(transaction.getValidity().getPriority()); - } - -} diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/Validity.java b/src/main/java/com/limechain/network/protocol/transaction/state/Validity.java deleted file mode 100644 index 35472343b..000000000 --- a/src/main/java/com/limechain/network/protocol/transaction/state/Validity.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.limechain.network.protocol.transaction.state; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import java.math.BigInteger; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -public class Validity { - private BigInteger priority; - private byte[][] requires; - private byte[][] provides; - private BigInteger longevity; - private boolean propagate; - - public Validity(BigInteger priority){ - this.priority = priority; - } -} diff --git a/src/main/java/com/limechain/network/protocol/warp/WarpSyncProtocol.java b/src/main/java/com/limechain/network/protocol/warp/WarpSyncProtocol.java index 839d10514..4c2fe0bba 100644 --- a/src/main/java/com/limechain/network/protocol/warp/WarpSyncProtocol.java +++ b/src/main/java/com/limechain/network/protocol/warp/WarpSyncProtocol.java @@ -1,11 +1,11 @@ package com.limechain.network.protocol.warp; +import com.limechain.exception.scale.ScaleEncodingException; import com.limechain.network.encoding.Leb128LengthFrameDecoder; import com.limechain.network.encoding.Leb128LengthFrameEncoder; import com.limechain.network.protocol.warp.dto.WarpSyncRequest; import com.limechain.network.protocol.warp.dto.WarpSyncResponse; import com.limechain.network.protocol.warp.encoding.WarpSyncResponseDecoder; -import com.limechain.exception.scale.ScaleEncodingException; import com.limechain.network.protocol.warp.scale.writer.WarpSyncRequestWriter; import io.emeraldpay.polkaj.scale.ScaleCodecWriter; import io.libp2p.core.Stream; diff --git a/src/main/java/com/limechain/network/protocol/warp/dto/BlockBody.java b/src/main/java/com/limechain/network/protocol/warp/dto/BlockBody.java index d11203166..9a7d9f4a4 100644 --- a/src/main/java/com/limechain/network/protocol/warp/dto/BlockBody.java +++ b/src/main/java/com/limechain/network/protocol/warp/dto/BlockBody.java @@ -1,6 +1,7 @@ package com.limechain.network.protocol.warp.dto; import com.limechain.network.protocol.warp.scale.writer.BlockBodyWriter; +import com.limechain.transaction.dto.Extrinsic; import com.limechain.utils.HashUtils; import com.limechain.utils.scale.ScaleUtils; import lombok.Data; @@ -10,11 +11,11 @@ @Data public class BlockBody { - private final List extrinsics; + private final List extrinsics; public byte[][] getExtrinsicsAsByteArray() { return extrinsics.stream() - .map(Extrinsics::getExtrinsic) + .map(Extrinsic::getData) .toArray(byte[][]::new); } diff --git a/src/main/java/com/limechain/network/protocol/warp/scale/reader/BlockBodyReader.java b/src/main/java/com/limechain/network/protocol/warp/scale/reader/BlockBodyReader.java index 5a84ca3fe..4c9e219d2 100644 --- a/src/main/java/com/limechain/network/protocol/warp/scale/reader/BlockBodyReader.java +++ b/src/main/java/com/limechain/network/protocol/warp/scale/reader/BlockBodyReader.java @@ -1,7 +1,7 @@ package com.limechain.network.protocol.warp.scale.reader; import com.limechain.network.protocol.warp.dto.BlockBody; -import com.limechain.network.protocol.warp.dto.Extrinsics; +import com.limechain.transaction.dto.Extrinsic; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleReader; import lombok.AccessLevel; @@ -21,12 +21,12 @@ public static BlockBodyReader getInstance() { @Override public BlockBody read(ScaleCodecReader reader) { - List extrinsics = new LinkedList<>(); + List extrinsics = new LinkedList<>(); int extrinsicsCount = reader.readCompactInt(); for (int i = 0; i < extrinsicsCount; i++) { byte[] extrinsic = reader.readByteArray(); - extrinsics.add(new Extrinsics(extrinsic)); + extrinsics.add(new Extrinsic(extrinsic)); } return new BlockBody(extrinsics); diff --git a/src/main/java/com/limechain/network/protocol/warp/scale/reader/JustificationReader.java b/src/main/java/com/limechain/network/protocol/warp/scale/reader/JustificationReader.java index 5814a0a3a..90df6b76f 100644 --- a/src/main/java/com/limechain/network/protocol/warp/scale/reader/JustificationReader.java +++ b/src/main/java/com/limechain/network/protocol/warp/scale/reader/JustificationReader.java @@ -1,8 +1,8 @@ package com.limechain.network.protocol.warp.scale.reader; import com.limechain.network.protocol.warp.dto.BlockHeader; -import com.limechain.network.protocol.warp.dto.Precommit; import com.limechain.network.protocol.warp.dto.Justification; +import com.limechain.network.protocol.warp.dto.Precommit; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleReader; import io.emeraldpay.polkaj.scale.reader.UInt64Reader; diff --git a/src/main/java/com/limechain/network/protocol/warp/scale/reader/WarpSyncFragmentReader.java b/src/main/java/com/limechain/network/protocol/warp/scale/reader/WarpSyncFragmentReader.java index 3ff2c8ce7..d78a8afb5 100644 --- a/src/main/java/com/limechain/network/protocol/warp/scale/reader/WarpSyncFragmentReader.java +++ b/src/main/java/com/limechain/network/protocol/warp/scale/reader/WarpSyncFragmentReader.java @@ -1,8 +1,6 @@ package com.limechain.network.protocol.warp.scale.reader; import com.limechain.network.protocol.warp.dto.WarpSyncFragment; -import com.limechain.network.protocol.warp.scale.reader.BlockHeaderReader; -import com.limechain.network.protocol.warp.scale.reader.JustificationReader; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleReader; diff --git a/src/main/java/com/limechain/network/protocol/warp/scale/writer/BlockBodyWriter.java b/src/main/java/com/limechain/network/protocol/warp/scale/writer/BlockBodyWriter.java index feb075da8..5cf5dd086 100644 --- a/src/main/java/com/limechain/network/protocol/warp/scale/writer/BlockBodyWriter.java +++ b/src/main/java/com/limechain/network/protocol/warp/scale/writer/BlockBodyWriter.java @@ -1,7 +1,7 @@ package com.limechain.network.protocol.warp.scale.writer; import com.limechain.network.protocol.warp.dto.BlockBody; -import com.limechain.network.protocol.warp.dto.Extrinsics; +import com.limechain.transaction.dto.Extrinsic; import io.emeraldpay.polkaj.scale.ScaleCodecWriter; import io.emeraldpay.polkaj.scale.ScaleWriter; import lombok.AccessLevel; @@ -21,11 +21,11 @@ public static BlockBodyWriter getInstance() { @Override public void write(ScaleCodecWriter writer, BlockBody blockBody) throws IOException { - List extrinsics = blockBody.getExtrinsics(); + List extrinsics = blockBody.getExtrinsics(); writer.writeCompact(extrinsics.size()); - for (Extrinsics extrinsic : extrinsics) { - writer.writeAsList(extrinsic.getExtrinsic()); + for (Extrinsic extrinsic : extrinsics) { + writer.writeAsList(extrinsic.getData()); } } } diff --git a/src/main/java/com/limechain/rpc/methods/chain/ChainRPCImpl.java b/src/main/java/com/limechain/rpc/methods/chain/ChainRPCImpl.java index da05e9fdf..081e19c64 100644 --- a/src/main/java/com/limechain/rpc/methods/chain/ChainRPCImpl.java +++ b/src/main/java/com/limechain/rpc/methods/chain/ChainRPCImpl.java @@ -1,12 +1,12 @@ package com.limechain.rpc.methods.chain; +import com.limechain.exception.storage.BlockNotFoundException; +import com.limechain.exception.storage.BlockStorageGenericException; +import com.limechain.exception.storage.HeaderNotFoundException; import com.limechain.network.protocol.warp.dto.Block; import com.limechain.network.protocol.warp.dto.BlockHeader; import com.limechain.network.protocol.warp.dto.HeaderDigest; import com.limechain.storage.block.BlockState; -import com.limechain.exception.storage.BlockNotFoundException; -import com.limechain.exception.storage.BlockStorageGenericException; -import com.limechain.exception.storage.HeaderNotFoundException; import com.limechain.utils.StringUtils; import io.emeraldpay.polkaj.types.Hash256; import lombok.AllArgsConstructor; diff --git a/src/main/java/com/limechain/rpc/server/RpcApp.java b/src/main/java/com/limechain/rpc/server/RpcApp.java index 49676ea38..277092c0c 100644 --- a/src/main/java/com/limechain/rpc/server/RpcApp.java +++ b/src/main/java/com/limechain/rpc/server/RpcApp.java @@ -18,11 +18,12 @@ */ @SpringBootApplication @ComponentScan(basePackages = { - "com.limechain.rpc.config", - "com.limechain.rpc.methods", - "com.limechain.rpc.server", - "com.limechain.storage", - "com.limechain.runtime.builder", + "com.limechain.rpc.config", + "com.limechain.rpc.methods", + "com.limechain.rpc.server", + "com.limechain.storage", + "com.limechain.runtime.builder", + "com.limechain.transaction" }) public class RpcApp { diff --git a/src/main/java/com/limechain/rpc/server/UnsafeInterceptor.java b/src/main/java/com/limechain/rpc/server/UnsafeInterceptor.java index 0ab4b818d..26bb2be37 100644 --- a/src/main/java/com/limechain/rpc/server/UnsafeInterceptor.java +++ b/src/main/java/com/limechain/rpc/server/UnsafeInterceptor.java @@ -3,8 +3,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.googlecode.jsonrpc4j.JsonRpcInterceptor; import com.limechain.cli.CliArguments; -import com.limechain.rpc.config.UnsafeRpcMethod; import com.limechain.exception.rpc.UnsafeAccessException; +import com.limechain.rpc.config.UnsafeRpcMethod; import java.lang.reflect.Method; import java.util.List; diff --git a/src/main/java/com/limechain/rpc/subscriptions/chainhead/ChainHeadRpcImpl.java b/src/main/java/com/limechain/rpc/subscriptions/chainhead/ChainHeadRpcImpl.java index 28159724e..47034bd06 100644 --- a/src/main/java/com/limechain/rpc/subscriptions/chainhead/ChainHeadRpcImpl.java +++ b/src/main/java/com/limechain/rpc/subscriptions/chainhead/ChainHeadRpcImpl.java @@ -1,12 +1,12 @@ package com.limechain.rpc.subscriptions.chainhead; -import com.limechain.exception.rpc.InvalidURIException; import com.limechain.exception.global.ThreadInterruptedException; +import com.limechain.exception.rpc.InvalidURIException; +import com.limechain.rpc.client.SubscriptionRpcClient; import com.limechain.rpc.config.SubscriptionName; import com.limechain.rpc.pubsub.Topic; import com.limechain.rpc.pubsub.publisher.PublisherImpl; import com.limechain.rpc.subscriptions.utils.Utils; -import com.limechain.rpc.client.SubscriptionRpcClient; import org.springframework.stereotype.Service; import java.net.URI; diff --git a/src/main/java/com/limechain/rpc/subscriptions/transaction/TransactionRpcImpl.java b/src/main/java/com/limechain/rpc/subscriptions/transaction/TransactionRpcImpl.java index 10b642bd5..7444e0b5b 100644 --- a/src/main/java/com/limechain/rpc/subscriptions/transaction/TransactionRpcImpl.java +++ b/src/main/java/com/limechain/rpc/subscriptions/transaction/TransactionRpcImpl.java @@ -1,12 +1,12 @@ package com.limechain.rpc.subscriptions.transaction; -import com.limechain.exception.rpc.InvalidURIException; import com.limechain.exception.global.ThreadInterruptedException; +import com.limechain.exception.rpc.InvalidURIException; +import com.limechain.rpc.client.SubscriptionRpcClient; import com.limechain.rpc.config.SubscriptionName; import com.limechain.rpc.pubsub.Topic; import com.limechain.rpc.pubsub.publisher.PublisherImpl; import com.limechain.rpc.subscriptions.utils.Utils; -import com.limechain.rpc.client.SubscriptionRpcClient; import java.net.URI; import java.net.URISyntaxException; diff --git a/src/main/java/com/limechain/runtime/Context.java b/src/main/java/com/limechain/runtime/Context.java index 9ed35ef39..43d7435c0 100644 --- a/src/main/java/com/limechain/runtime/Context.java +++ b/src/main/java/com/limechain/runtime/Context.java @@ -1,9 +1,9 @@ package com.limechain.runtime; import com.limechain.runtime.hostapi.dto.OffchainNetworkState; -import com.limechain.storage.offchain.OffchainStorages; import com.limechain.runtime.version.RuntimeVersion; import com.limechain.storage.crypto.KeyStore; +import com.limechain.storage.offchain.OffchainStorages; import com.limechain.trie.TrieAccessor; import lombok.AccessLevel; import lombok.AllArgsConstructor; diff --git a/src/main/java/com/limechain/runtime/Runtime.java b/src/main/java/com/limechain/runtime/Runtime.java index f2751bc20..5936cba4d 100644 --- a/src/main/java/com/limechain/runtime/Runtime.java +++ b/src/main/java/com/limechain/runtime/Runtime.java @@ -29,6 +29,7 @@ public class Runtime { /** * Calls an exported runtime function with no parameters. + * * @param function the name Runtime function to call * @return the SCALE encoded response */ @@ -39,7 +40,8 @@ public byte[] call(RuntimeEndpoint function) { /** * Calls an exported runtime function with parameters. - * @param function the name Runtime function to call + * + * @param function the name Runtime function to call * @param parameter the SCALE encoded tuple of parameters * @return the SCALE encoded response */ @@ -53,7 +55,7 @@ private byte[] callInner(RuntimeEndpoint function, RuntimePointerSize parameterP String functionName = function.getName(); log.log(Level.FINE, "Making a runtime call: " + functionName); Object[] response = instance.exports.getFunction(functionName) - .apply(parameterPtrSize.pointer(), parameterPtrSize.size()); + .apply(parameterPtrSize.pointer(), parameterPtrSize.size()); if (response == null) { return null; @@ -77,6 +79,7 @@ RuntimeVersion callCoreVersion() { /** * Executes the block by calling `Core_execute_block`. + * * @param block the block to execute * @return the SCALE encoded result of the runtime call */ @@ -87,8 +90,8 @@ public byte[] executeBlock(Block block) { private static byte[] serializeExecuteBlockParameter(Block block) { byte[] encodedUnsealedHeader = ScaleUtils.Encode.encode( - BlockHeaderScaleWriter.getInstance()::writeUnsealed, - block.getHeader() + BlockHeaderScaleWriter.getInstance()::writeUnsealed, + block.getHeader() ); byte[] encodedBody = ScaleUtils.Encode.encode(BlockBodyWriter.getInstance(), block.getBody()); @@ -97,7 +100,8 @@ private static byte[] serializeExecuteBlockParameter(Block block) { /** * Checks whether the provided inherents are valid for the block by calling `BlockBuilder_Check_inherents - * @param block the block to check against + * + * @param block the block to check against * @param inherentData inherents to check for validity * @return the SCALE encoded result of the runtime call */ diff --git a/src/main/java/com/limechain/runtime/RuntimeEndpoint.java b/src/main/java/com/limechain/runtime/RuntimeEndpoint.java index 3572c0dac..377125993 100644 --- a/src/main/java/com/limechain/runtime/RuntimeEndpoint.java +++ b/src/main/java/com/limechain/runtime/RuntimeEndpoint.java @@ -5,6 +5,7 @@ /** * Used to identify runtime API endpoints by their names as listed in the spec. + * * @see the spec */ // NOTE: Add here whatever endpoints necessary during development. @@ -15,6 +16,7 @@ public enum RuntimeEndpoint { CORE_VERSION("Core_version"), BLOCKBUILDER_CHECK_INHERENTS("BlockBuilder_check_inherents"), METADATA_METADATA("Metadata_metadata"), + TRANSACTION_QUEUE_VALIDATE_TRANSACTION("TaggedTransactionQueue_validate_transaction"), ; private final String name; diff --git a/src/main/java/com/limechain/runtime/allocator/Header.java b/src/main/java/com/limechain/runtime/allocator/Header.java index 4beb2cfa3..9fec4c67b 100644 --- a/src/main/java/com/limechain/runtime/allocator/Header.java +++ b/src/main/java/com/limechain/runtime/allocator/Header.java @@ -1,10 +1,10 @@ package com.limechain.runtime.allocator; +import com.limechain.runtime.memory.Memory; import lombok.AccessLevel; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.java.Log; -import com.limechain.runtime.memory.Memory; /** * Allocation header preceding a memory block. diff --git a/src/main/java/com/limechain/runtime/version/ApiVersionName.java b/src/main/java/com/limechain/runtime/version/ApiVersionName.java new file mode 100644 index 000000000..d04aa7d14 --- /dev/null +++ b/src/main/java/com/limechain/runtime/version/ApiVersionName.java @@ -0,0 +1,21 @@ +package com.limechain.runtime.version; + +import com.limechain.utils.HashUtils; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ApiVersionName { + + AURA_API("AuraApi"), + BABE_API("BabeApi"), + GRANDPA_API("GrandpaApi"), + TRANSACTION_QUEUE_API("TaggedTransactionQueue"); + + private final String name; + + public byte[] getHashedName() { + return HashUtils.hashWithBlake2bToLength(name.getBytes(), ApiVersion.NAME_HASH_LENGTH); + } +} diff --git a/src/main/java/com/limechain/runtime/version/scale/RuntimeVersionReader.java b/src/main/java/com/limechain/runtime/version/scale/RuntimeVersionReader.java index e561792af..c121421a4 100644 --- a/src/main/java/com/limechain/runtime/version/scale/RuntimeVersionReader.java +++ b/src/main/java/com/limechain/runtime/version/scale/RuntimeVersionReader.java @@ -1,8 +1,8 @@ package com.limechain.runtime.version.scale; +import com.limechain.runtime.version.ApiVersions; import com.limechain.runtime.version.RuntimeVersion; import com.limechain.runtime.version.StateVersion; -import com.limechain.runtime.version.ApiVersions; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleReader; diff --git a/src/main/java/com/limechain/storage/block/BlockStateHelper.java b/src/main/java/com/limechain/storage/block/BlockStateHelper.java index bb35d9046..ccb11b846 100644 --- a/src/main/java/com/limechain/storage/block/BlockStateHelper.java +++ b/src/main/java/com/limechain/storage/block/BlockStateHelper.java @@ -1,8 +1,8 @@ package com.limechain.storage.block; +import com.limechain.exception.scale.ScaleEncodingException; import com.limechain.network.protocol.blockannounce.scale.BlockHeaderScaleWriter; import com.limechain.network.protocol.warp.dto.BlockHeader; -import com.limechain.exception.scale.ScaleEncodingException; import com.limechain.network.protocol.warp.scale.reader.BlockHeaderReader; import com.limechain.storage.DBConstants; import io.emeraldpay.polkaj.scale.ScaleCodecReader; diff --git a/src/main/java/com/limechain/sync/fullsync/FullSyncMachine.java b/src/main/java/com/limechain/sync/fullsync/FullSyncMachine.java index 04605a593..3274cbfbf 100644 --- a/src/main/java/com/limechain/sync/fullsync/FullSyncMachine.java +++ b/src/main/java/com/limechain/sync/fullsync/FullSyncMachine.java @@ -10,7 +10,6 @@ import com.limechain.network.protocol.warp.dto.BlockBody; import com.limechain.network.protocol.warp.dto.BlockHeader; import com.limechain.network.protocol.warp.dto.DigestType; -import com.limechain.network.protocol.warp.dto.Extrinsics; import com.limechain.network.protocol.warp.dto.HeaderDigest; import com.limechain.network.protocol.warp.scale.reader.BlockHeaderReader; import com.limechain.rpc.server.AppBean; @@ -21,12 +20,15 @@ import com.limechain.storage.block.SyncState; import com.limechain.storage.trie.TrieStorage; import com.limechain.sync.fullsync.inherents.InherentData; +import com.limechain.transaction.TransactionValidator; +import com.limechain.transaction.dto.Extrinsic; import com.limechain.trie.DiskTrieAccessor; import com.limechain.trie.TrieAccessor; import com.limechain.trie.TrieStructureFactory; import com.limechain.trie.structure.TrieStructure; import com.limechain.trie.structure.database.NodeData; import com.limechain.trie.structure.nibble.Nibbles; +import com.limechain.utils.StringUtils; import com.limechain.utils.scale.ScaleUtils; import com.limechain.utils.scale.readers.PairReader; import io.emeraldpay.polkaj.scale.ScaleCodecReader; @@ -89,8 +91,8 @@ public void start() { blockState.storeRuntime(lastFinalizedBlockHash, runtime); int startNumber = syncState.getLastFinalizedBlockNumber() - .add(BigInteger.ONE) - .intValue(); + .add(BigInteger.ONE) + .intValue(); int blocksToFetch = 100; List receivedBlocks = requestBlocks(startNumber, blocksToFetch); @@ -98,7 +100,7 @@ public void start() { while (!receivedBlocks.isEmpty()) { executeBlocks(receivedBlocks, trieAccessor); log.info("Executed blocks from " + receivedBlocks.getFirst().getHeader().getBlockNumber() - + " to " + receivedBlocks.getLast().getHeader().getBlockNumber()); + + " to " + receivedBlocks.getLast().getHeader().getBlockNumber()); startNumber += blocksToFetch; receivedBlocks = requestBlocks(startNumber, blocksToFetch); } @@ -164,18 +166,18 @@ private List requestBlocks(int start, int amount) { final int BODY = 0b0000_0010; final int JUSTIFICATION = 0b0001_0000; SyncMessage.BlockResponse response = networkService.makeBlockRequest(new BlockRequestDto( - HEADER | BODY | JUSTIFICATION, - null, // no hash, number instead - start, - SyncMessage.Direction.Ascending, - amount + HEADER | BODY | JUSTIFICATION, + null, // no hash, number instead + start, + SyncMessage.Direction.Ascending, + amount )); List blockDatas = response.getBlocksList(); return blockDatas.stream() - .map(FullSyncMachine::protobufDecodeBlock) - .toList(); + .map(FullSyncMachine::protobufDecodeBlock) + .toList(); } catch (Exception ex) { log.info("Error while fetching blocks, trying to fetch again"); if (!this.networkService.updateCurrentSelectedPeerWithNextBootnode()) { @@ -192,10 +194,10 @@ private static Block protobufDecodeBlock(SyncMessage.BlockData blockData) { BlockHeader blockHeader = ScaleUtils.Decode.decode(encodedHeader, new BlockHeaderReader()); // Protobuf decode the block body - List extrinsicsList = blockData.getBodyList().stream() - .map(bs -> ScaleUtils.Decode.decode(bs.toByteArray(), ScaleCodecReader::readByteArray)) - .map(Extrinsics::new) - .toList(); + List extrinsicsList = blockData.getBodyList().stream() + .map(bs -> ScaleUtils.Decode.decode(bs.toByteArray(), ScaleCodecReader::readByteArray)) + .map(Extrinsic::new) + .toList(); BlockBody blockBody = new BlockBody(extrinsicsList); @@ -245,8 +247,8 @@ private void executeBlocks(List receivedBlockDatas, TrieAccessor trieAcce } boolean blockUpdatedRuntime = Arrays.stream(blockHeader.getDigest()) - .map(HeaderDigest::getType) - .anyMatch(type -> type.equals(DigestType.RUN_ENV_UPDATED)); + .map(HeaderDigest::getType) + .anyMatch(type -> type.equals(DigestType.RUN_ENV_UPDATED)); if (blockUpdatedRuntime) { log.info("Runtime updated, updating the runtime code"); @@ -259,9 +261,9 @@ private void executeBlocks(List receivedBlockDatas, TrieAccessor trieAcce private Runtime buildRuntimeFromState(TrieAccessor trieAccessor) { return trieAccessor - .findStorageValue(Nibbles.fromBytes(":code".getBytes())) - .map(wasm -> runtimeBuilder.buildRuntime(wasm, trieAccessor)) - .orElseThrow(() -> new RuntimeException("Runtime code not found in the trie")); + .findStorageValue(Nibbles.fromBytes(":code".getBytes())) + .map(wasm -> runtimeBuilder.buildRuntime(wasm, trieAccessor)) + .orElseThrow(() -> new RuntimeException("Runtime code not found in the trie")); } private boolean checkInherents(Block block) { @@ -281,13 +283,13 @@ private boolean checkInherents(Block block) { */ private static boolean isBlockGoodToExecute(byte[] checkInherentsOutput) { var data = ScaleUtils.Decode.decode( - ArrayUtils.subarray(checkInherentsOutput, 2, checkInherentsOutput.length), - new ListReader<>( - new PairReader<>( - scr -> new String(scr.readByteArray(8)), - scr -> new String(scr.readByteArray()) + ArrayUtils.subarray(checkInherentsOutput, 2, checkInherentsOutput.length), + new ListReader<>( + new PairReader<>( + scr -> new String(scr.readByteArray(8)), + scr -> new String(scr.readByteArray()) + ) ) - ) ); boolean goodToExecute; diff --git a/src/main/java/com/limechain/sync/warpsync/action/ChainInformationBuildAction.java b/src/main/java/com/limechain/sync/warpsync/action/ChainInformationBuildAction.java index 347ef124b..94cdcec8b 100644 --- a/src/main/java/com/limechain/sync/warpsync/action/ChainInformationBuildAction.java +++ b/src/main/java/com/limechain/sync/warpsync/action/ChainInformationBuildAction.java @@ -1,10 +1,10 @@ package com.limechain.sync.warpsync.action; import com.limechain.rpc.server.AppBean; -import com.limechain.runtime.version.ApiVersion; +import com.limechain.runtime.version.ApiVersionName; +import com.limechain.runtime.version.ApiVersions; import com.limechain.sync.warpsync.WarpSyncMachine; import com.limechain.sync.warpsync.WarpSyncState; -import com.limechain.utils.HashUtils; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.extern.java.Log; @@ -31,23 +31,19 @@ public void next(WarpSyncMachine sync) { @Override public void handle(WarpSyncMachine sync) { - byte[][] hashedApiVersions = new byte[][]{ - HashUtils.hashWithBlake2bToLength("AuraApi".getBytes(), ApiVersion.NAME_HASH_LENGTH), - HashUtils.hashWithBlake2bToLength("BabeApi".getBytes(), ApiVersion.NAME_HASH_LENGTH), - HashUtils.hashWithBlake2bToLength("GrandpaApi".getBytes(), ApiVersion.NAME_HASH_LENGTH) - }; + ApiVersions versions = warpSyncState.getRuntime().getVersion().getApis(); sync.getChainInformation().setRuntimeAuraVersion( - warpSyncState.getRuntime().getVersion().getApis().getApiVersion(hashedApiVersions[0])); + versions.getApiVersion(ApiVersionName.AURA_API.getHashedName())); sync.getChainInformation().setRuntimeBabeVersion( - warpSyncState.getRuntime().getVersion().getApis().getApiVersion(hashedApiVersions[1])); + versions.getApiVersion(ApiVersionName.BABE_API.getHashedName())); sync.getChainInformation().setRuntimeGrandpaVersion( - warpSyncState.getRuntime().getVersion().getApis().getApiVersion(hashedApiVersions[2])); + versions.getApiVersion(ApiVersionName.GRANDPA_API.getHashedName())); log.log(Level.INFO, "Aura Api version: " + sync.getChainInformation().getRuntimeAuraVersion() - + " Babe api version: " + sync.getChainInformation().getRuntimeBabeVersion() + - " Grandpa Api Version: " + sync.getChainInformation().getRuntimeGrandpaVersion()); + + " Babe api version: " + sync.getChainInformation().getRuntimeBabeVersion() + + " Grandpa Api Version: " + sync.getChainInformation().getRuntimeGrandpaVersion()); log.log(Level.INFO, "Runtime supports aura: " + sync.getChainInformation().runtimeHasAura()); log.log(Level.INFO, "Runtime babe api is v1: " + sync.getChainInformation().runtimeBabeApiIsV1()); log.log(Level.INFO, "Runtime grandpa supports current setId: " - + sync.getChainInformation().runtimeGrandpaSupportsCurrentSetId()); + + sync.getChainInformation().runtimeGrandpaSupportsCurrentSetId()); } } diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/Pool.java b/src/main/java/com/limechain/transaction/TransactionPool.java similarity index 60% rename from src/main/java/com/limechain/network/protocol/transaction/state/Pool.java rename to src/main/java/com/limechain/transaction/TransactionPool.java index b2b107ecd..2f72fc52c 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/Pool.java +++ b/src/main/java/com/limechain/transaction/TransactionPool.java @@ -1,17 +1,17 @@ -package com.limechain.network.protocol.transaction.state; +package com.limechain.transaction; +import com.limechain.transaction.dto.ValidTransaction; import com.limechain.utils.HashUtils; -import lombok.NoArgsConstructor; import java.util.HashMap; import java.util.Map; -@NoArgsConstructor -public class Pool { +public class TransactionPool { + final Map transactions = new HashMap<>(); - public ValidTransaction get(byte[] extrinisics) { - byte[] key = HashUtils.hashWithBlake2b(extrinisics); + public ValidTransaction get(byte[] extrinsics) { + byte[] key = HashUtils.hashWithBlake2b(extrinsics); return transactions.get(key); } @@ -20,18 +20,18 @@ public ValidTransaction[] transactions() { return transactions.values().toArray(ValidTransaction[]::new); } - public byte[] insert(ValidTransaction validTransaction){ - byte[] key = HashUtils.hashWithBlake2b(validTransaction.getExtrinsic()); + public byte[] insert(ValidTransaction validTransaction) { + byte[] key = HashUtils.hashWithBlake2b(validTransaction.extrinsic().getData()); transactions.put(key, validTransaction); return key; } - public void removeExtrinsic(byte[] extrinsic){ + public void removeExtrinsic(byte[] extrinsic) { byte[] key = HashUtils.hashWithBlake2b(extrinsic); transactions.remove(key); } - public int length(){ + public int length() { return transactions.size(); } } diff --git a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java b/src/main/java/com/limechain/transaction/TransactionState.java similarity index 92% rename from src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java rename to src/main/java/com/limechain/transaction/TransactionState.java index 98a2c6176..f409f639c 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/state/TransactionState.java +++ b/src/main/java/com/limechain/transaction/TransactionState.java @@ -1,5 +1,7 @@ -package com.limechain.network.protocol.transaction.state; +package com.limechain.transaction; +import com.limechain.transaction.dto.Extrinsic; +import com.limechain.transaction.dto.ValidTransaction; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -19,7 +21,7 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class TransactionState { private static final TransactionState INSTANCE = new TransactionState(); - private final Pool transactionPool = new Pool(); + private final TransactionPool transactionPool = new TransactionPool(); private final ExecutorService executor = Executors.newSingleThreadExecutor(); @Getter @Setter @@ -87,7 +89,7 @@ public boolean exists(ValidTransaction validTransaction) { public void removeExtrinsic(byte[] extrinsic) { transactionPool.removeExtrinsic(extrinsic); - ValidTransaction transactionToBeRemoved = new ValidTransaction(extrinsic); + ValidTransaction transactionToBeRemoved = new ValidTransaction(new Extrinsic(extrinsic), null); transactionQueue.remove(transactionToBeRemoved); } @@ -98,6 +100,4 @@ public void removeExtrinsicFromPool(byte[] extrinsic) { public byte[] addToPool(ValidTransaction validTransaction) { return transactionPool.insert(validTransaction); } - - //Todo: notifyStatus ? } diff --git a/src/main/java/com/limechain/transaction/TransactionValidator.java b/src/main/java/com/limechain/transaction/TransactionValidator.java new file mode 100644 index 000000000..402ce393f --- /dev/null +++ b/src/main/java/com/limechain/transaction/TransactionValidator.java @@ -0,0 +1,61 @@ +package com.limechain.transaction; + +import com.limechain.exception.misc.RuntimeApiVersionException; +import com.limechain.runtime.Runtime; +import com.limechain.runtime.RuntimeEndpoint; +import com.limechain.runtime.version.ApiVersionName; +import com.limechain.storage.block.BlockState; +import com.limechain.transaction.dto.Extrinsic; +import com.limechain.transaction.dto.TransactionSource; +import com.limechain.transaction.dto.TransactionValidationRequest; +import com.limechain.transaction.dto.TransactionValidationResponse; +import com.limechain.transaction.dto.ValidTransaction; +import com.limechain.utils.scale.ScaleUtils; +import com.limechain.utils.scale.readers.TransactionValidationReader; +import com.limechain.utils.scale.writers.TransactionValidationWriter; +import lombok.extern.java.Log; +import org.springframework.stereotype.Component; + +import java.math.BigInteger; + +@Component +@Log +public class TransactionValidator { + + private final BlockState blockState; + + public TransactionValidator() { + this.blockState = BlockState.getInstance(); + } + + public TransactionValidationResponse validateTransaction(Runtime runtime, Extrinsic transaction) { + byte[] scaleRequest = createValidationRequest(runtime, transaction); + byte[] validationResult = runtime.call( + RuntimeEndpoint.TRANSACTION_QUEUE_VALIDATE_TRANSACTION, scaleRequest); + return ScaleUtils.Decode.decode(validationResult, new TransactionValidationReader()); + } + + private byte[] createValidationRequest(Runtime runtime, Extrinsic transaction) { + BigInteger txQueueVersion = runtime.getVersion().getApis() + .getApiVersion(ApiVersionName.TRANSACTION_QUEUE_API.getHashedName()); + + TransactionValidationRequest request = new TransactionValidationRequest(); + + switch (txQueueVersion.intValue()) { + case 1 -> request.setTransaction(transaction.getData()); + case 2 -> { + request.setSource(TransactionSource.EXTERNAL); + request.setTransaction(transaction.getData()); + } + case 3 -> { + request.setSource(TransactionSource.EXTERNAL); + request.setTransaction(transaction.getData()); + request.setParentBlockHash(blockState.bestBlockHash()); + } + default -> throw new RuntimeApiVersionException( + String.format("Invalid transaction queue version: %d", txQueueVersion)); + } + + return ScaleUtils.Encode.encode(new TransactionValidationWriter(), request); + } +} diff --git a/src/main/java/com/limechain/network/protocol/warp/dto/Extrinsics.java b/src/main/java/com/limechain/transaction/dto/Extrinsic.java similarity index 77% rename from src/main/java/com/limechain/network/protocol/warp/dto/Extrinsics.java rename to src/main/java/com/limechain/transaction/dto/Extrinsic.java index bd4c6bb73..16ec794aa 100644 --- a/src/main/java/com/limechain/network/protocol/warp/dto/Extrinsics.java +++ b/src/main/java/com/limechain/transaction/dto/Extrinsic.java @@ -1,4 +1,4 @@ -package com.limechain.network.protocol.warp.dto; +package com.limechain.transaction.dto; import com.limechain.exception.scale.ScaleEncodingException; import com.limechain.utils.HashUtils; @@ -10,19 +10,19 @@ import java.io.IOException; @Data -public class Extrinsics { +public class Extrinsic { - private final byte[] extrinsic; + private final byte[] data; @Override public String toString() { - return HexUtils.toHexString(extrinsic); + return HexUtils.toHexString(data); } public byte[] getHash() { try (ByteArrayOutputStream buf = new ByteArrayOutputStream(); ScaleCodecWriter writer = new ScaleCodecWriter(buf)) { - writer.writeAsList(extrinsic); + writer.writeAsList(data); return HashUtils.hashWithBlake2b(buf.toByteArray()); } catch (IOException e) { throw new ScaleEncodingException(e); diff --git a/src/main/java/com/limechain/transaction/dto/ExtrinsicArray.java b/src/main/java/com/limechain/transaction/dto/ExtrinsicArray.java new file mode 100644 index 000000000..1e02933de --- /dev/null +++ b/src/main/java/com/limechain/transaction/dto/ExtrinsicArray.java @@ -0,0 +1,11 @@ +package com.limechain.transaction.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class ExtrinsicArray { + + private Extrinsic[] extrinsics; +} diff --git a/src/main/java/com/limechain/transaction/dto/InvalidTransactionType.java b/src/main/java/com/limechain/transaction/dto/InvalidTransactionType.java new file mode 100644 index 000000000..a6c35091f --- /dev/null +++ b/src/main/java/com/limechain/transaction/dto/InvalidTransactionType.java @@ -0,0 +1,40 @@ +package com.limechain.transaction.dto; + +import org.springframework.lang.Nullable; + +import java.util.Arrays; + +public enum InvalidTransactionType implements TransactionValidityError { + + CALL_NOT_EXPECTED(0, true), + INABILITY_TO_PAY_FEES(1, true), + TRANSACTION_NOT_YET_VALID(2, false), + TRANSACTION_OUTDATED(3, true), + INVALID_PROOF(4, true), + ANCIENT_BIRTH_BLOCK(5, true), + EXHAUST_BLOCK_RESOURCES(6, false), + UNKNOWN_ERROR(7, true), + MANDATORY_DISPATCH_ERROR(8, true), + INVALID_MANDATORY_DISPATCH(9, true); + + private final int id; + private final boolean shouldReject; + + InvalidTransactionType(int id, boolean shouldReject) { + this.id = id; + this.shouldReject = shouldReject; + } + + @Nullable + public static TransactionValidityError getFromInt(int intValue) { + return Arrays.stream(values()) + .filter(v -> v.id == intValue) + .findFirst() + .orElse(null); + } + + @Override + public boolean shouldReject() { + return shouldReject; + } +} diff --git a/src/main/java/com/limechain/transaction/dto/TransactionSource.java b/src/main/java/com/limechain/transaction/dto/TransactionSource.java new file mode 100644 index 000000000..af27bf359 --- /dev/null +++ b/src/main/java/com/limechain/transaction/dto/TransactionSource.java @@ -0,0 +1,33 @@ +package com.limechain.transaction.dto; + +/** + * The source of the transaction. + */ +public enum TransactionSource { + /** + * Transaction is already included in a block. + *

+ * This means that we can't really tell where the transaction is coming from, + * since it's already in the received block. Note that the custom validation logic + * using either `Local` or `External` should most likely just allow `InBlock` + * transactions as well. + */ + IN_BLOCK, + + /** + * Transaction is coming from a local source. + *

+ * This means that the transaction was produced internally by the node + * (for instance an Off-Chain Worker or an Off-Chain Call), as opposed + * to being received over the network. + */ + LOCAL, + + /** + * Transaction has been received externally. + *

+ * This means the transaction has been received from (usually) an "untrusted" source, + * for instance received over the network or RPC. + */ + EXTERNAL; +} diff --git a/src/main/java/com/limechain/transaction/dto/TransactionValidationRequest.java b/src/main/java/com/limechain/transaction/dto/TransactionValidationRequest.java new file mode 100644 index 000000000..9c4b4eee4 --- /dev/null +++ b/src/main/java/com/limechain/transaction/dto/TransactionValidationRequest.java @@ -0,0 +1,17 @@ +package com.limechain.transaction.dto; + +import io.emeraldpay.polkaj.types.Hash256; +import lombok.Data; +import org.jetbrains.annotations.Nullable; + +@Data +public class TransactionValidationRequest { + + private byte[] transaction; + + @Nullable + private TransactionSource source; + + @Nullable + private Hash256 parentBlockHash; +} diff --git a/src/main/java/com/limechain/transaction/dto/TransactionValidationResponse.java b/src/main/java/com/limechain/transaction/dto/TransactionValidationResponse.java new file mode 100644 index 000000000..7e63c92b0 --- /dev/null +++ b/src/main/java/com/limechain/transaction/dto/TransactionValidationResponse.java @@ -0,0 +1,14 @@ +package com.limechain.transaction.dto; + +import lombok.Data; +import org.jetbrains.annotations.Nullable; + +@Data +public class TransactionValidationResponse { + + @Nullable + TransactionValidity validTx; + + @Nullable + TransactionValidityError transactionValidityError; +} diff --git a/src/main/java/com/limechain/transaction/dto/TransactionValidity.java b/src/main/java/com/limechain/transaction/dto/TransactionValidity.java new file mode 100644 index 000000000..55e8756fb --- /dev/null +++ b/src/main/java/com/limechain/transaction/dto/TransactionValidity.java @@ -0,0 +1,15 @@ +package com.limechain.transaction.dto; + +import lombok.Data; + +import java.math.BigInteger; + +@Data +public class TransactionValidity { + + private BigInteger priority; + private byte[][] requires; + private byte[][] provides; + private BigInteger longevity; + private Boolean propagate; +} diff --git a/src/main/java/com/limechain/transaction/dto/TransactionValidityError.java b/src/main/java/com/limechain/transaction/dto/TransactionValidityError.java new file mode 100644 index 000000000..dd6b3caac --- /dev/null +++ b/src/main/java/com/limechain/transaction/dto/TransactionValidityError.java @@ -0,0 +1,6 @@ +package com.limechain.transaction.dto; + +public interface TransactionValidityError { + + boolean shouldReject(); +} diff --git a/src/main/java/com/limechain/transaction/dto/UnknownTransactionType.java b/src/main/java/com/limechain/transaction/dto/UnknownTransactionType.java new file mode 100644 index 000000000..316d30872 --- /dev/null +++ b/src/main/java/com/limechain/transaction/dto/UnknownTransactionType.java @@ -0,0 +1,33 @@ +package com.limechain.transaction.dto; + +import org.springframework.lang.Nullable; + +import java.util.Arrays; + +public enum UnknownTransactionType implements TransactionValidityError { + + METADATA_SEARCH_FAILURE(0, true), + NO_VALIDATOR_FOUND(1, true), + UNKNOWN_VALIDITY(2, false); + + private final int id; + private final boolean shouldReject; + + UnknownTransactionType(int id, boolean shouldReject) { + this.id = id; + this.shouldReject = shouldReject; + } + + @Nullable + public static TransactionValidityError getFromInt(int intValue) { + return Arrays.stream(values()) + .filter(v -> v.id == intValue) + .findFirst() + .orElse(null); + } + + @Override + public boolean shouldReject() { + return shouldReject; + } +} diff --git a/src/main/java/com/limechain/transaction/dto/ValidTransaction.java b/src/main/java/com/limechain/transaction/dto/ValidTransaction.java new file mode 100644 index 000000000..84ece2340 --- /dev/null +++ b/src/main/java/com/limechain/transaction/dto/ValidTransaction.java @@ -0,0 +1,15 @@ +package com.limechain.transaction.dto; + +import lombok.EqualsAndHashCode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public record ValidTransaction(@EqualsAndHashCode.Include Extrinsic extrinsic, @Nullable TransactionValidity transactionValidity) + implements Comparable { + + public int compareTo(@NotNull ValidTransaction transaction) { + return this.transactionValidity().getPriority() + .compareTo(transaction.transactionValidity().getPriority()); + } + +} diff --git a/src/main/java/com/limechain/trie/decoded/Node.java b/src/main/java/com/limechain/trie/decoded/Node.java index 4dfc5a4d7..a8d4abe8c 100644 --- a/src/main/java/com/limechain/trie/decoded/Node.java +++ b/src/main/java/com/limechain/trie/decoded/Node.java @@ -1,7 +1,7 @@ package com.limechain.trie.decoded; -import com.limechain.trie.decoded.encoder.TrieEncoder; import com.limechain.exception.trie.TrieEncoderException; +import com.limechain.trie.decoded.encoder.TrieEncoder; import com.limechain.utils.HashUtils; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/limechain/trie/dto/node/DecodedNode.java b/src/main/java/com/limechain/trie/dto/node/DecodedNode.java index bac7046cf..9ffd4bf5f 100644 --- a/src/main/java/com/limechain/trie/dto/node/DecodedNode.java +++ b/src/main/java/com/limechain/trie/dto/node/DecodedNode.java @@ -1,8 +1,8 @@ package com.limechain.trie.dto.node; -import com.limechain.trie.decoded.NodeVariant; import com.limechain.exception.trie.NodeDecodingException; import com.limechain.exception.trie.NodeEncodingException; +import com.limechain.trie.decoded.NodeVariant; import com.limechain.trie.structure.nibble.BytesToNibbles; import com.limechain.trie.structure.nibble.Nibble; import com.limechain.trie.structure.nibble.Nibbles; diff --git a/src/main/java/com/limechain/utils/scale/readers/PairReader.java b/src/main/java/com/limechain/utils/scale/readers/PairReader.java index 24ebd18c8..f2e01f95c 100644 --- a/src/main/java/com/limechain/utils/scale/readers/PairReader.java +++ b/src/main/java/com/limechain/utils/scale/readers/PairReader.java @@ -2,8 +2,8 @@ import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleReader; -import org.javatuples.Pair; import lombok.AllArgsConstructor; +import org.javatuples.Pair; @AllArgsConstructor public class PairReader implements ScaleReader> { diff --git a/src/main/java/com/limechain/utils/scale/readers/TransactionValidationReader.java b/src/main/java/com/limechain/utils/scale/readers/TransactionValidationReader.java new file mode 100644 index 000000000..731f18023 --- /dev/null +++ b/src/main/java/com/limechain/utils/scale/readers/TransactionValidationReader.java @@ -0,0 +1,59 @@ +package com.limechain.utils.scale.readers; + +import com.limechain.transaction.dto.InvalidTransactionType; +import com.limechain.transaction.dto.TransactionValidationResponse; +import com.limechain.transaction.dto.TransactionValidity; +import com.limechain.transaction.dto.TransactionValidityError; +import com.limechain.transaction.dto.UnknownTransactionType; +import io.emeraldpay.polkaj.scale.ScaleCodecReader; +import io.emeraldpay.polkaj.scale.ScaleReader; +import io.emeraldpay.polkaj.scale.reader.UInt64Reader; + +import java.util.Objects; + +public class TransactionValidationReader implements ScaleReader { + + @Override + public TransactionValidationResponse read(ScaleCodecReader reader) { + TransactionValidationResponse response = new TransactionValidationResponse(); + + try { + TransactionValidity validity = new TransactionValidity(); + + validity.setPriority(new UInt64Reader().read(reader)); + + int requiresCount = reader.readCompactInt(); + byte[][] requires = new byte[requiresCount][]; + for (int i = 0; i < requiresCount; i++) { + requires[i] = reader.readByteArray(); + } + validity.setRequires(requires); + + int providesCount = reader.readCompactInt(); + byte[][] provides = new byte[providesCount][]; + for (int i = 0; i < providesCount; i++) { + provides[i] = reader.readByteArray(); + } + validity.setProvides(provides); + + validity.setLongevity(new UInt64Reader().read(reader)); + validity.setPropagate(reader.readCompactInt() != 0); + + response.setValidTx(validity); + } catch (Exception e) { + response.setValidTx(null); + } + + if (Objects.isNull(response.getValidTx())) { + int errorType = reader.readCompactInt(); + int errorInt = reader.readCompactInt(); + TransactionValidityError error = errorType == 0 + ? InvalidTransactionType.getFromInt(errorInt) + : UnknownTransactionType.getFromInt(errorInt); + + response.setTransactionValidityError(error); + } + + return response; + } +} diff --git a/src/main/java/com/limechain/utils/scale/writers/IterableWriter.java b/src/main/java/com/limechain/utils/scale/writers/IterableWriter.java index 24894ef9b..f8179693f 100644 --- a/src/main/java/com/limechain/utils/scale/writers/IterableWriter.java +++ b/src/main/java/com/limechain/utils/scale/writers/IterableWriter.java @@ -5,7 +5,6 @@ import org.apache.commons.collections4.IterableUtils; import java.io.IOException; -import java.util.Iterator; public class IterableWriter implements ScaleWriter> { private final ScaleWriter scaleWriter; diff --git a/src/main/java/com/limechain/utils/scale/writers/TransactionValidationWriter.java b/src/main/java/com/limechain/utils/scale/writers/TransactionValidationWriter.java new file mode 100644 index 000000000..1af7bf2e5 --- /dev/null +++ b/src/main/java/com/limechain/utils/scale/writers/TransactionValidationWriter.java @@ -0,0 +1,24 @@ +package com.limechain.utils.scale.writers; + +import com.limechain.transaction.dto.TransactionValidationRequest; +import io.emeraldpay.polkaj.scale.ScaleCodecWriter; +import io.emeraldpay.polkaj.scale.ScaleWriter; + +import java.io.IOException; +import java.util.Objects; + +public class TransactionValidationWriter implements ScaleWriter { + + @Override + public void write(ScaleCodecWriter writer, TransactionValidationRequest request) throws IOException { + if (!Objects.isNull(request.getSource())) { + writer.writeByte(request.getSource().ordinal()); + } + + writer.writeByteArray(request.getTransaction()); + + if (!Objects.isNull(request.getParentBlockHash())) { + writer.writeByteArray(request.getParentBlockHash().getBytes()); + } + } +} diff --git a/src/test/java/com/limechain/network/protocol/blockannounce/BlockAnnounceControllerTest.java b/src/test/java/com/limechain/network/protocol/blockannounce/BlockAnnounceControllerTest.java index 887e8aae8..696a52623 100644 --- a/src/test/java/com/limechain/network/protocol/blockannounce/BlockAnnounceControllerTest.java +++ b/src/test/java/com/limechain/network/protocol/blockannounce/BlockAnnounceControllerTest.java @@ -1,6 +1,5 @@ package com.limechain.network.protocol.blockannounce; -import com.limechain.network.protocol.blockannounce.messages.BlockAnnounceHandshake; import com.limechain.network.protocol.blockannounce.messages.BlockAnnounceHandshakeBuilder; import io.libp2p.core.PeerId; import io.libp2p.core.Stream; @@ -9,7 +8,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.verify; diff --git a/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java b/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java index 33aec44a0..eaee32493 100644 --- a/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java +++ b/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java @@ -1,8 +1,11 @@ package com.limechain.network.protocol.transaction.state; +import com.limechain.transaction.TransactionState; +import com.limechain.transaction.dto.Extrinsic; +import com.limechain.transaction.dto.ValidTransaction; +import com.limechain.transaction.dto.TransactionValidity; import org.junit.jupiter.api.Test; -import java.math.BigInteger; import java.util.Arrays; import java.util.Comparator; @@ -14,20 +17,20 @@ class TransactionStateTest { void testTransactionState() { TransactionState transactionState = TransactionState.getInstance(); ValidTransaction[] validTransactions = new ValidTransaction[]{ - new ValidTransaction(new byte[]{'a'}, new Validity(BigInteger.ONE)), - new ValidTransaction(new byte[]{'b'}, new Validity(BigInteger.valueOf(4))), - new ValidTransaction(new byte[]{'c'}, new Validity(BigInteger.valueOf(2))), - new ValidTransaction(new byte[]{'d'}, new Validity(BigInteger.valueOf(17))), - new ValidTransaction(new byte[]{'e'}, new Validity(BigInteger.valueOf(2))), + new ValidTransaction(new Extrinsic(new byte[]{'a'}), new TransactionValidity()), + new ValidTransaction(new Extrinsic(new byte[]{'b'}), new TransactionValidity()), + new ValidTransaction(new Extrinsic(new byte[]{'c'}), new TransactionValidity()), + new ValidTransaction(new Extrinsic(new byte[]{'d'}), new TransactionValidity()), + new ValidTransaction(new Extrinsic(new byte[]{'e'}), new TransactionValidity()) }; - for (ValidTransaction validTransaction: - validTransactions) { + for (ValidTransaction validTransaction : + validTransactions) { transactionState.addToPool(validTransaction); } ValidTransaction[] pendingInPool = transactionState.pendingInPool(); - Arrays.sort(pendingInPool, Comparator.comparing(a -> new String(a.getExtrinsic()))); + Arrays.sort(pendingInPool, Comparator.comparing(a -> new String(a.extrinsic().getData()))); assertArrayEquals(validTransactions, pendingInPool); assertNull(transactionState.peek()); diff --git a/src/test/java/com/limechain/runtime/hostapi/OffchainHostFunctionsTest.java b/src/test/java/com/limechain/runtime/hostapi/OffchainHostFunctionsTest.java index f7a0e9ba7..50bbb37e2 100644 --- a/src/test/java/com/limechain/runtime/hostapi/OffchainHostFunctionsTest.java +++ b/src/test/java/com/limechain/runtime/hostapi/OffchainHostFunctionsTest.java @@ -1,12 +1,12 @@ package com.limechain.runtime.hostapi; +import com.limechain.exception.hostapi.InvalidArgumentException; import com.limechain.runtime.SharedMemory; -import com.limechain.runtime.hostapi.dto.OffchainNetworkState; -import com.limechain.storage.offchain.OffchainStorages; import com.limechain.runtime.hostapi.dto.HttpErrorType; import com.limechain.runtime.hostapi.dto.HttpStatusCode; -import com.limechain.exception.hostapi.InvalidArgumentException; +import com.limechain.runtime.hostapi.dto.OffchainNetworkState; import com.limechain.runtime.hostapi.dto.RuntimePointerSize; +import com.limechain.storage.offchain.OffchainStorages; import com.limechain.storage.offchain.OffchainStore; import com.limechain.utils.scale.ScaleUtils; import io.emeraldpay.polkaj.scale.ScaleCodecWriter; diff --git a/src/test/java/com/limechain/storage/DBInitializerTest.java b/src/test/java/com/limechain/storage/DBInitializerTest.java index 4b341889e..04a99f925 100644 --- a/src/test/java/com/limechain/storage/DBInitializerTest.java +++ b/src/test/java/com/limechain/storage/DBInitializerTest.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; import sun.misc.Unsafe; import java.lang.reflect.Field; diff --git a/src/test/java/com/limechain/sync/fullsync/BlockExecutorTest.java b/src/test/java/com/limechain/sync/fullsync/BlockExecutorTest.java index 6b9fd1339..65d98777e 100644 --- a/src/test/java/com/limechain/sync/fullsync/BlockExecutorTest.java +++ b/src/test/java/com/limechain/sync/fullsync/BlockExecutorTest.java @@ -5,7 +5,7 @@ import com.limechain.network.protocol.warp.dto.Block; import com.limechain.network.protocol.warp.dto.BlockBody; import com.limechain.network.protocol.warp.dto.BlockHeader; -import com.limechain.network.protocol.warp.dto.Extrinsics; +import com.limechain.transaction.dto.Extrinsic; import com.limechain.network.protocol.warp.dto.HeaderDigest; import com.limechain.network.protocol.warp.scale.reader.HeaderDigestReader; import com.limechain.runtime.Runtime; @@ -75,7 +75,7 @@ private Block getKusamaFirstBlock() { var exts = ScaleUtils.Decode.decodeList(scaleEncodedBody, ScaleCodecReader::readByteArray); assertEquals(2, exts.size()); - BlockBody body = new BlockBody(exts.stream().map(Extrinsics::new).toList()); + BlockBody body = new BlockBody(exts.stream().map(Extrinsic::new).toList()); return new Block(header, body); } diff --git a/src/test/java/com/limechain/trie/structure/slab/SlabTest.java b/src/test/java/com/limechain/trie/structure/slab/SlabTest.java index 7cf0d6ee4..bee35d6ca 100644 --- a/src/test/java/com/limechain/trie/structure/slab/SlabTest.java +++ b/src/test/java/com/limechain/trie/structure/slab/SlabTest.java @@ -4,11 +4,11 @@ import org.javatuples.Pair; import org.junit.jupiter.api.Test; -import java.util.Set; +import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; -import java.util.ArrayList; import java.util.List; +import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; From 20ef88a4abbc14b80dafd004d30f05247d5c27ba Mon Sep 17 00:00:00 2001 From: Yordan Atanasov Date: Thu, 17 Oct 2024 13:52:54 +0300 Subject: [PATCH 17/19] feat: Implement transaction queue and pool population logic. --- .../TransactionValidationException.java | 7 ++ .../java/com/limechain/network/Network.java | 5 +- .../transaction/TransactionsEngine.java | 65 +++++++++--- .../transaction/TransactionsService.java | 4 +- .../transaction/TransactionPool.java | 19 ++-- .../transaction/TransactionState.java | 54 ++++++---- .../transaction/TransactionValidator.java | 29 ++--- .../limechain/transaction/dto/Extrinsic.java | 23 +--- .../transaction/dto/TransactionValidity.java | 4 + .../transaction/dto/ValidTransaction.java | 22 +++- .../com/limechain/utils/ByteArrayUtils.java | 21 +++- .../readers/TransactionValidationReader.java | 4 +- .../state/TransactionStateTest.java | 100 ++++++++++++++++-- .../limechain/utils/ByteArrayUtilsTest.java | 96 +++++++++++++++++ 14 files changed, 362 insertions(+), 91 deletions(-) create mode 100644 src/main/java/com/limechain/exception/transaction/TransactionValidationException.java diff --git a/src/main/java/com/limechain/exception/transaction/TransactionValidationException.java b/src/main/java/com/limechain/exception/transaction/TransactionValidationException.java new file mode 100644 index 000000000..cf2a16d83 --- /dev/null +++ b/src/main/java/com/limechain/exception/transaction/TransactionValidationException.java @@ -0,0 +1,7 @@ +package com.limechain.exception.transaction; + +public class TransactionValidationException extends RuntimeException { + public TransactionValidationException(String message) { + super(message); + } +} diff --git a/src/main/java/com/limechain/network/Network.java b/src/main/java/com/limechain/network/Network.java index 8e183ee52..859389a7d 100644 --- a/src/main/java/com/limechain/network/Network.java +++ b/src/main/java/com/limechain/network/Network.java @@ -256,7 +256,7 @@ public void findPeers() { if (getPeersCount() >= REPLICATION) { log.log(Level.INFO, "Connections have reached replication factor(" + REPLICATION + "). " + - "No need to search for new ones yet."); + "No need to search for new ones yet."); return; } @@ -373,7 +373,8 @@ public void sendNeighbourMessages() { if (!AppBean.getBean(WarpSyncState.class).isWarpSyncFinished()) { return; } - connectionManager.getPeerIds().forEach(peerId -> grandpaService.sendNeighbourMessage(this.host, peerId)); + connectionManager.getPeerIds().forEach(peerId -> + grandpaService.sendNeighbourMessage(this.host, peerId)); connectionManager.getPeerIds().forEach(peerId -> transactionsService.sendTransactionsMessage(this.host, peerId)); } diff --git a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java index 6d1702b6f..1c20493bb 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java @@ -1,15 +1,20 @@ package com.limechain.network.protocol.transaction; import com.limechain.exception.scale.ScaleEncodingException; +import com.limechain.exception.transaction.TransactionValidationException; import com.limechain.network.ConnectionManager; import com.limechain.network.protocol.transaction.scale.TransactionsReader; import com.limechain.network.protocol.transaction.scale.TransactionsWriter; import com.limechain.network.protocol.warp.dto.BlockHeader; +import com.limechain.rpc.server.AppBean; import com.limechain.runtime.Runtime; import com.limechain.storage.block.BlockState; import com.limechain.sync.warpsync.WarpSyncState; +import com.limechain.transaction.TransactionState; +import com.limechain.transaction.TransactionValidator; import com.limechain.transaction.dto.Extrinsic; import com.limechain.transaction.dto.ExtrinsicArray; +import com.limechain.transaction.dto.ValidTransaction; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleCodecWriter; import io.libp2p.core.PeerId; @@ -25,9 +30,18 @@ */ @Log public class TransactionsEngine { + private static final int HANDSHAKE_LENGTH = 1; - private final ConnectionManager connectionManager = ConnectionManager.getInstance(); + private final ConnectionManager connectionManager; + private final TransactionState transactionState; + private final BlockState blockState; + + public TransactionsEngine() { + connectionManager = ConnectionManager.getInstance(); + transactionState = AppBean.getBean(TransactionState.class); + blockState = BlockState.getInstance(); + } /** * Handles an incoming request as follows: @@ -69,6 +83,7 @@ private void handleInitiatorStreamMessage(byte[] message, Stream stream) { } connectionManager.addTransactionsStream(stream); log.log(Level.INFO, "Received transactions handshake from " + peerId); + stream.writeAndFlush(new byte[]{}); //TODO *2nd* Send valid transactions to the peer we received a handshake from } @@ -106,18 +121,41 @@ private void handleTransactionMessage(byte[] message, PeerId peerId) { log.log(Level.INFO, "Received " + transactions.getExtrinsics().length + " transactions from Peer " + peerId); - final BlockHeader header = BlockState.getInstance().bestBlockHeader(); - if (header == null) { - log.log(Level.WARNING, "No best block header found"); - return; - } - - final Runtime runtime = BlockState.getInstance().getRuntime(header.getHash()); - if (runtime == null) { - log.log(Level.WARNING, "No runtime found for block header " + header.getHash()); + for (int i = 0; i < transactions.getExtrinsics().length; i++) { + Extrinsic current = transactions.getExtrinsics()[i]; + if (transactionState.existsInQueue(current) || transactionState.existsInPool(current)) { + continue; + } + + final BlockHeader header = blockState.bestBlockHeader(); + if (header == null) { + log.log(Level.WARNING, "No best block header found"); + return; + } + + final Runtime runtime = blockState.getRuntime(header.getHash()); + if (runtime == null) { + log.log(Level.WARNING, "No runtime found for block header " + header.getHash()); + return; + } + + ValidTransaction validTransaction; + try { + validTransaction = TransactionValidator.validateTransaction(runtime, header.getHash(), current); + validTransaction.getIgnore().add(peerId); + } catch (TransactionValidationException e) { + log.fine("Error when validating transaction " + current.toString() + + " from protocol: " + e.getMessage()); + + continue; + } + + if (transactionState.shouldAddToQueue(validTransaction)) { + transactionState.pushTransaction(validTransaction); + } else { + transactionState.addToPool(validTransaction); + } } - //TODO *1st* Validate transaction using runtime and then add to transaction pool - // (depends on StateStorage and TrieState) } /** @@ -150,8 +188,7 @@ public void writeTransactionsMessage(Stream stream, PeerId peerId) { } log.log(Level.INFO, "Sending transaction message to peer " + peerId); - //TODO *3rd* Send our transaction message - // (this is probably used after an extrinsic is received via an RPC call or created internally). + //TODO send transaction message containing non repetitive transactions for peer. } private boolean isHandshake(byte[] message) { diff --git a/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java index 630a2dfed..6eb6184cf 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java @@ -25,6 +25,7 @@ public TransactionsService(String protocolId) { * @param peerId message receiver */ public void sendTransactionsMessage(Host us, PeerId peerId) { + // TODO Network improvements Keep track of peers that we've notified about each transaction Optional.ofNullable(connectionManager.getPeerInfo(peerId)) .map(p -> p.getTransactionsStreams().getInitiator()) .ifPresentOrElse( @@ -34,8 +35,7 @@ public void sendTransactionsMessage(Host us, PeerId peerId) { } private void sendTransactions(Stream stream) { - //TODO *Last* Send transaction messages - // (scheduled task to notify peers about transactions in the TP and TQ). + //TODO Send transaction messages } private void sendHandshake(Host us, PeerId peerId) { diff --git a/src/main/java/com/limechain/transaction/TransactionPool.java b/src/main/java/com/limechain/transaction/TransactionPool.java index 2f72fc52c..a054749a9 100644 --- a/src/main/java/com/limechain/transaction/TransactionPool.java +++ b/src/main/java/com/limechain/transaction/TransactionPool.java @@ -1,5 +1,6 @@ package com.limechain.transaction; +import com.limechain.transaction.dto.Extrinsic; import com.limechain.transaction.dto.ValidTransaction; import com.limechain.utils.HashUtils; @@ -8,11 +9,10 @@ public class TransactionPool { - final Map transactions = new HashMap<>(); - - public ValidTransaction get(byte[] extrinsics) { - byte[] key = HashUtils.hashWithBlake2b(extrinsics); + private final Map transactions = new HashMap<>(); + public ValidTransaction get(Extrinsic extrinsic) { + byte[] key = HashUtils.hashWithBlake2b(extrinsic.getData()); return transactions.get(key); } @@ -21,17 +21,22 @@ public ValidTransaction[] transactions() { } public byte[] insert(ValidTransaction validTransaction) { - byte[] key = HashUtils.hashWithBlake2b(validTransaction.extrinsic().getData()); + byte[] key = HashUtils.hashWithBlake2b(validTransaction.getExtrinsic().getData()); transactions.put(key, validTransaction); return key; } - public void removeExtrinsic(byte[] extrinsic) { - byte[] key = HashUtils.hashWithBlake2b(extrinsic); + public void removeExtrinsic(Extrinsic extrinsic) { + byte[] key = HashUtils.hashWithBlake2b(extrinsic.getData()); transactions.remove(key); } public int length() { return transactions.size(); } + + public boolean exists(Extrinsic extrinsic) { + byte[] key = HashUtils.hashWithBlake2b(extrinsic.getData()); + return transactions.containsKey(key); + } } diff --git a/src/main/java/com/limechain/transaction/TransactionState.java b/src/main/java/com/limechain/transaction/TransactionState.java index f409f639c..ce588169c 100644 --- a/src/main/java/com/limechain/transaction/TransactionState.java +++ b/src/main/java/com/limechain/transaction/TransactionState.java @@ -2,33 +2,34 @@ import com.limechain.transaction.dto.Extrinsic; import com.limechain.transaction.dto.ValidTransaction; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import com.limechain.utils.ByteArrayUtils; import lombok.extern.java.Log; import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; +import java.util.Arrays; import java.util.PriorityQueue; import java.util.Queue; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; @Log -@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Component public class TransactionState { - private static final TransactionState INSTANCE = new TransactionState(); - private final TransactionPool transactionPool = new TransactionPool(); - private final ExecutorService executor = Executors.newSingleThreadExecutor(); - @Getter - @Setter - private Queue transactionQueue = new PriorityQueue<>(); - - public static TransactionState getInstance() { - return INSTANCE; + + private final TransactionPool transactionPool; + private final ExecutorService executor; + private final Queue transactionQueue; + + public TransactionState() { + transactionPool = new TransactionPool(); + executor = Executors.newSingleThreadExecutor(); + transactionQueue = new PriorityQueue<>(); } public void pushTransaction(ValidTransaction validTransaction) { @@ -83,17 +84,32 @@ public ValidTransaction[] pendingInPool() { return transactionPool.transactions(); } - public boolean exists(ValidTransaction validTransaction) { - return transactionQueue.contains(validTransaction); + public boolean existsInQueue(Extrinsic extrinsic) { + return transactionQueue.contains(new ValidTransaction(extrinsic, null)); + } + + public boolean existsInPool(Extrinsic extrinsic) { + return transactionPool.exists(extrinsic); + } + + public boolean shouldAddToQueue(ValidTransaction validTransaction) { + Set provided = transactionQueue.stream() + .flatMap(entry -> Arrays.stream(entry.getTransactionValidity().getProvides())) + .collect(Collectors.toSet()); + + Set required = Arrays.stream(validTransaction.getTransactionValidity().getRequires()) + .collect(Collectors.toSet()); + + return ByteArrayUtils.sourceContainsAll(provided, required); } - public void removeExtrinsic(byte[] extrinsic) { + public void removeExtrinsic(Extrinsic extrinsic) { transactionPool.removeExtrinsic(extrinsic); - ValidTransaction transactionToBeRemoved = new ValidTransaction(new Extrinsic(extrinsic), null); + ValidTransaction transactionToBeRemoved = new ValidTransaction(extrinsic, null); transactionQueue.remove(transactionToBeRemoved); } - public void removeExtrinsicFromPool(byte[] extrinsic) { + public void removeExtrinsicFromPool(Extrinsic extrinsic) { transactionPool.removeExtrinsic(extrinsic); } diff --git a/src/main/java/com/limechain/transaction/TransactionValidator.java b/src/main/java/com/limechain/transaction/TransactionValidator.java index 402ce393f..8c894b571 100644 --- a/src/main/java/com/limechain/transaction/TransactionValidator.java +++ b/src/main/java/com/limechain/transaction/TransactionValidator.java @@ -1,10 +1,10 @@ package com.limechain.transaction; import com.limechain.exception.misc.RuntimeApiVersionException; +import com.limechain.exception.transaction.TransactionValidationException; import com.limechain.runtime.Runtime; import com.limechain.runtime.RuntimeEndpoint; import com.limechain.runtime.version.ApiVersionName; -import com.limechain.storage.block.BlockState; import com.limechain.transaction.dto.Extrinsic; import com.limechain.transaction.dto.TransactionSource; import com.limechain.transaction.dto.TransactionValidationRequest; @@ -13,29 +13,30 @@ import com.limechain.utils.scale.ScaleUtils; import com.limechain.utils.scale.readers.TransactionValidationReader; import com.limechain.utils.scale.writers.TransactionValidationWriter; +import io.emeraldpay.polkaj.types.Hash256; import lombok.extern.java.Log; -import org.springframework.stereotype.Component; import java.math.BigInteger; +import java.util.Objects; -@Component @Log public class TransactionValidator { - private final BlockState blockState; - - public TransactionValidator() { - this.blockState = BlockState.getInstance(); - } - - public TransactionValidationResponse validateTransaction(Runtime runtime, Extrinsic transaction) { - byte[] scaleRequest = createValidationRequest(runtime, transaction); + public static ValidTransaction validateTransaction(Runtime runtime, Hash256 hash, Extrinsic transaction) { + byte[] scaleRequest = createValidationRequest(runtime, hash, transaction); byte[] validationResult = runtime.call( RuntimeEndpoint.TRANSACTION_QUEUE_VALIDATE_TRANSACTION, scaleRequest); - return ScaleUtils.Decode.decode(validationResult, new TransactionValidationReader()); + TransactionValidationResponse response = + ScaleUtils.Decode.decode(validationResult, new TransactionValidationReader()); + + if (!Objects.isNull(response.getTransactionValidityError())) { + throw new TransactionValidationException(response.getTransactionValidityError().toString()); + } + + return new ValidTransaction(transaction, response.getValidTx()); } - private byte[] createValidationRequest(Runtime runtime, Extrinsic transaction) { + private static byte[] createValidationRequest(Runtime runtime, Hash256 hash256, Extrinsic transaction) { BigInteger txQueueVersion = runtime.getVersion().getApis() .getApiVersion(ApiVersionName.TRANSACTION_QUEUE_API.getHashedName()); @@ -50,7 +51,7 @@ private byte[] createValidationRequest(Runtime runtime, Extrinsic transaction) { case 3 -> { request.setSource(TransactionSource.EXTERNAL); request.setTransaction(transaction.getData()); - request.setParentBlockHash(blockState.bestBlockHash()); + request.setParentBlockHash(hash256); } default -> throw new RuntimeApiVersionException( String.format("Invalid transaction queue version: %d", txQueueVersion)); diff --git a/src/main/java/com/limechain/transaction/dto/Extrinsic.java b/src/main/java/com/limechain/transaction/dto/Extrinsic.java index 16ec794aa..1a09f3e55 100644 --- a/src/main/java/com/limechain/transaction/dto/Extrinsic.java +++ b/src/main/java/com/limechain/transaction/dto/Extrinsic.java @@ -1,32 +1,15 @@ package com.limechain.transaction.dto; -import com.limechain.exception.scale.ScaleEncodingException; -import com.limechain.utils.HashUtils; -import io.emeraldpay.polkaj.scale.ScaleCodecWriter; -import lombok.Data; +import lombok.Value; import org.apache.tomcat.util.buf.HexUtils; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -@Data +@Value public class Extrinsic { - private final byte[] data; + byte[] data; @Override public String toString() { return HexUtils.toHexString(data); } - - public byte[] getHash() { - try (ByteArrayOutputStream buf = new ByteArrayOutputStream(); - ScaleCodecWriter writer = new ScaleCodecWriter(buf)) { - writer.writeAsList(data); - return HashUtils.hashWithBlake2b(buf.toByteArray()); - } catch (IOException e) { - throw new ScaleEncodingException(e); - } - } - } diff --git a/src/main/java/com/limechain/transaction/dto/TransactionValidity.java b/src/main/java/com/limechain/transaction/dto/TransactionValidity.java index 55e8756fb..506c60fee 100644 --- a/src/main/java/com/limechain/transaction/dto/TransactionValidity.java +++ b/src/main/java/com/limechain/transaction/dto/TransactionValidity.java @@ -1,10 +1,14 @@ package com.limechain.transaction.dto; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import java.math.BigInteger; @Data +@AllArgsConstructor +@NoArgsConstructor public class TransactionValidity { private BigInteger priority; diff --git a/src/main/java/com/limechain/transaction/dto/ValidTransaction.java b/src/main/java/com/limechain/transaction/dto/ValidTransaction.java index 84ece2340..7b9fe89fc 100644 --- a/src/main/java/com/limechain/transaction/dto/ValidTransaction.java +++ b/src/main/java/com/limechain/transaction/dto/ValidTransaction.java @@ -1,15 +1,27 @@ package com.limechain.transaction.dto; +import io.libp2p.core.PeerId; import lombok.EqualsAndHashCode; +import lombok.Value; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public record ValidTransaction(@EqualsAndHashCode.Include Extrinsic extrinsic, @Nullable TransactionValidity transactionValidity) - implements Comparable { +import java.util.HashSet; + +@Value +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +public class ValidTransaction implements Comparable { + + @EqualsAndHashCode.Include + Extrinsic extrinsic; + + @Nullable + TransactionValidity transactionValidity; + + HashSet ignore = new HashSet<>(); public int compareTo(@NotNull ValidTransaction transaction) { - return this.transactionValidity().getPriority() - .compareTo(transaction.transactionValidity().getPriority()); + return this.transactionValidity.getPriority() + .compareTo(transaction.transactionValidity.getPriority()); } - } diff --git a/src/main/java/com/limechain/utils/ByteArrayUtils.java b/src/main/java/com/limechain/utils/ByteArrayUtils.java index 879b62195..b40e90286 100644 --- a/src/main/java/com/limechain/utils/ByteArrayUtils.java +++ b/src/main/java/com/limechain/utils/ByteArrayUtils.java @@ -2,6 +2,9 @@ import lombok.experimental.UtilityClass; +import java.util.Arrays; +import java.util.Collection; + @UtilityClass public class ByteArrayUtils { @@ -19,7 +22,7 @@ public class ByteArrayUtils { */ public static int commonPrefixLength(byte[] a, byte[] b) { int minLength = Math.min(a.length, b.length); - if(minLength == 0) { + if (minLength == 0) { return 0; } int length; @@ -100,4 +103,20 @@ public static byte[] concatenate(byte[] prefix, byte[] suffix) { System.arraycopy(suffix, 0, result, prefix.length, suffix.length); return result; } + + /** + * Checks if all the byte arrays in the target collection are present in the source collection. + * + * @param source the collection of byte arrays that may contain the target arrays + * @param target the collection of byte arrays to check for presence in the source + * @return {@code true} if all byte arrays in the target collection are present in the source + * collection, {@code false} otherwise + */ + public boolean sourceContainsAll(Collection source, Collection target) { + return target.stream() + .allMatch(targetArray -> + source.stream() + .anyMatch(sourceArray -> Arrays.equals(sourceArray, targetArray)) + ); + } } diff --git a/src/main/java/com/limechain/utils/scale/readers/TransactionValidationReader.java b/src/main/java/com/limechain/utils/scale/readers/TransactionValidationReader.java index 731f18023..6099e8256 100644 --- a/src/main/java/com/limechain/utils/scale/readers/TransactionValidationReader.java +++ b/src/main/java/com/limechain/utils/scale/readers/TransactionValidationReader.java @@ -13,6 +13,8 @@ public class TransactionValidationReader implements ScaleReader { + private static final int INVALID_TRANSACTION_TYPE = 0; + @Override public TransactionValidationResponse read(ScaleCodecReader reader) { TransactionValidationResponse response = new TransactionValidationResponse(); @@ -47,7 +49,7 @@ public TransactionValidationResponse read(ScaleCodecReader reader) { if (Objects.isNull(response.getValidTx())) { int errorType = reader.readCompactInt(); int errorInt = reader.readCompactInt(); - TransactionValidityError error = errorType == 0 + TransactionValidityError error = errorType == INVALID_TRANSACTION_TYPE ? InvalidTransactionType.getFromInt(errorInt) : UnknownTransactionType.getFromInt(errorInt); diff --git a/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java b/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java index eaee32493..f299c8e7d 100644 --- a/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java +++ b/src/test/java/com/limechain/network/protocol/transaction/state/TransactionStateTest.java @@ -2,20 +2,34 @@ import com.limechain.transaction.TransactionState; import com.limechain.transaction.dto.Extrinsic; -import com.limechain.transaction.dto.ValidTransaction; import com.limechain.transaction.dto.TransactionValidity; +import com.limechain.transaction.dto.ValidTransaction; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.math.BigInteger; import java.util.Arrays; import java.util.Comparator; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; class TransactionStateTest { + + private static final byte[] TAG_1 = "tag1".getBytes(); + private static final byte[] TAG_2 = "tag2".getBytes(); + + private TransactionState sut; + + @BeforeEach + void setUp() { + sut = new TransactionState(); + } + @Test void testTransactionState() { - TransactionState transactionState = TransactionState.getInstance(); ValidTransaction[] validTransactions = new ValidTransaction[]{ new ValidTransaction(new Extrinsic(new byte[]{'a'}), new TransactionValidity()), new ValidTransaction(new Extrinsic(new byte[]{'b'}), new TransactionValidity()), @@ -25,14 +39,88 @@ void testTransactionState() { }; for (ValidTransaction validTransaction : validTransactions) { - transactionState.addToPool(validTransaction); + sut.addToPool(validTransaction); } - ValidTransaction[] pendingInPool = transactionState.pendingInPool(); + ValidTransaction[] pendingInPool = sut.pendingInPool(); - Arrays.sort(pendingInPool, Comparator.comparing(a -> new String(a.extrinsic().getData()))); + Arrays.sort(pendingInPool, Comparator.comparing(a -> new String(a.getExtrinsic().getData()))); assertArrayEquals(validTransactions, pendingInPool); - assertNull(transactionState.peek()); + assertNull(sut.peek()); + } + + @Test + void testShouldAddToQueue_AllRequiredProvided() { + TransactionValidity validity1 = new TransactionValidity( + BigInteger.TEN, null, new byte[][]{TAG_1, TAG_2}, BigInteger.ZERO, true); + ValidTransaction existingTransaction = new ValidTransaction(null, validity1); + + sut.pushTransaction(existingTransaction); + + TransactionValidity validity2 = new TransactionValidity( + BigInteger.ONE, new byte[][]{TAG_1, TAG_2}, null, BigInteger.ZERO, true); + ValidTransaction validTransaction = new ValidTransaction(null, validity2); + + boolean result = sut.shouldAddToQueue(validTransaction); + assertTrue(result); + } + + @Test + void testShouldAddToQueue_NotAllRequiredProvided() { + TransactionValidity validity1 = new TransactionValidity( + BigInteger.TEN, null, new byte[][]{TAG_1}, BigInteger.ZERO, true); + ValidTransaction existingTransaction = new ValidTransaction(null, validity1); + + sut.pushTransaction(existingTransaction); + + TransactionValidity validity2 = new TransactionValidity( + BigInteger.ONE, new byte[][]{TAG_1, TAG_2}, null, BigInteger.ZERO, true); + ValidTransaction validTransaction = new ValidTransaction(null, validity2); + + boolean result = sut.shouldAddToQueue(validTransaction); + assertFalse(result); + } + + @Test + void testShouldAddToQueue_EmptyTransactionQueue() { + TransactionValidity validity2 = new TransactionValidity( + BigInteger.ONE, new byte[][]{TAG_1}, null, BigInteger.ZERO, true); + ValidTransaction validTransaction = new ValidTransaction(null, validity2); + + boolean result = sut.shouldAddToQueue(validTransaction); + assertFalse(result); + } + + @Test + void testShouldAddToQueue_TransactionHasNoRequires() { + TransactionValidity validity1 = new TransactionValidity( + BigInteger.TEN, null, new byte[][]{TAG_1}, BigInteger.ZERO, true); + ValidTransaction existingTransaction = new ValidTransaction(null, validity1); + + sut.pushTransaction(existingTransaction); + + TransactionValidity validity2 = new TransactionValidity( + BigInteger.ONE, new byte[][]{}, null, BigInteger.ZERO, true); + ValidTransaction validTransaction = new ValidTransaction(null, validity2); + + boolean result = sut.shouldAddToQueue(validTransaction); + assertTrue(result); + } + + @Test + void testShouldAddToQueue_TransactionHasNoProvides() { + TransactionValidity validity1 = new TransactionValidity( + BigInteger.TEN, null, new byte[][]{}, BigInteger.ZERO, true); + ValidTransaction existingTransaction = new ValidTransaction(null, validity1); + + sut.pushTransaction(existingTransaction); + + TransactionValidity validity2 = new TransactionValidity( + BigInteger.ONE, new byte[][]{TAG_1}, null, BigInteger.ZERO, true); + ValidTransaction validTransaction = new ValidTransaction(null, validity2); + + boolean result = sut.shouldAddToQueue(validTransaction); + assertFalse(result); } } diff --git a/src/test/java/com/limechain/utils/ByteArrayUtilsTest.java b/src/test/java/com/limechain/utils/ByteArrayUtilsTest.java index f8c7dd118..733bc3c3e 100644 --- a/src/test/java/com/limechain/utils/ByteArrayUtilsTest.java +++ b/src/test/java/com/limechain/utils/ByteArrayUtilsTest.java @@ -2,9 +2,21 @@ import org.junit.jupiter.api.Test; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; class ByteArrayUtilsTest { + + private static final byte[] ELEMENT_1 = "element1".getBytes(); + private static final byte[] ELEMENT_2 = "element2".getBytes(); + private static final byte[] ELEMENT_3 = "element3".getBytes(); + + @Test void testTargetFound() { // Test with both arrays non-null and target found @@ -48,4 +60,88 @@ void testBothNull() { // Test with both arrays null assertEquals(-1, ByteArrayUtils.indexOf(null, null)); } + + @Test + void testSourceContainsAll_AllTargetElementsInSource() { + Collection source = List.of( + ELEMENT_1, + ELEMENT_2, + ELEMENT_3 + ); + + Collection target = List.of( + ELEMENT_1, + ELEMENT_2 + ); + + boolean result = ByteArrayUtils.sourceContainsAll(source, target); + assertTrue(result); + } + + @Test + void testSourceContainsAll_SourceHasExtraElements() { + Collection source = List.of( + ELEMENT_1, + ELEMENT_2, + ELEMENT_3, + "extraElement".getBytes() + ); + + Collection target = List.of( + ELEMENT_1, + ELEMENT_2 + ); + + boolean result = ByteArrayUtils.sourceContainsAll(source, target); + assertTrue(result); + } + + @Test + void testSourceContainsAll_MissingElementInSource() { + Collection source = List.of( + ELEMENT_1, + ELEMENT_2 + ); + + Collection target = List.of( + ELEMENT_1, + ELEMENT_2, + "missingElement".getBytes() + ); + + boolean result = ByteArrayUtils.sourceContainsAll(source, target); + assertFalse(result); + } + + @Test + void testSourceContainsAll_EmptySource() { + Collection source = Collections.emptyList(); + + Collection target = List.of( + ELEMENT_1 + ); + + boolean result = ByteArrayUtils.sourceContainsAll(source, target); + assertFalse(result); + } + + @Test + void testSourceContainsAll_EmptyTarget() { + Collection source = List.of( + ELEMENT_1, + ELEMENT_2 + ); + Collection target = Collections.emptyList(); + + boolean result = ByteArrayUtils.sourceContainsAll(source, target); + assertTrue(result); + } + + @Test + void testSourceContainsAll_BothEmpty() { + Collection source = Collections.emptyList(); + Collection target = Collections.emptyList(); + boolean result = ByteArrayUtils.sourceContainsAll(source, target); + assertTrue(result); // Both empty collections should result in true + } } \ No newline at end of file From c5d1c6df80a81f96af26be167d3aa2b511b3813f Mon Sep 17 00:00:00 2001 From: Yordan Atanasov Date: Mon, 21 Oct 2024 17:24:34 +0300 Subject: [PATCH 18/19] refactor: Move block announce handshake at start of full sync. Handshake all peers at end of full sync. --- .../TransactionValidationException.java | 2 +- .../java/com/limechain/network/Network.java | 29 ++++------ .../blockannounce/BlockAnnounceService.java | 10 ++-- .../protocol/grandpa/GrandpaService.java | 7 ++- .../transaction/TransactionsEngine.java | 26 ++------- .../transaction/TransactionsService.java | 2 +- .../sync/fullsync/FullSyncMachine.java | 5 +- .../sync/warpsync/WarpSyncMachine.java | 1 - .../sync/warpsync/WarpSyncState.java | 5 +- .../transaction/TransactionValidator.java | 54 +++++++++++++---- .../com/limechain/utils/scale/ScaleUtils.java | 58 +++++++++++-------- .../readers/TransactionValidationReader.java | 17 ++---- .../writers/TransactionValidationWriter.java | 2 +- .../BlockAnnounceServiceTest.java | 5 +- 14 files changed, 117 insertions(+), 106 deletions(-) diff --git a/src/main/java/com/limechain/exception/transaction/TransactionValidationException.java b/src/main/java/com/limechain/exception/transaction/TransactionValidationException.java index cf2a16d83..a0fe3a447 100644 --- a/src/main/java/com/limechain/exception/transaction/TransactionValidationException.java +++ b/src/main/java/com/limechain/exception/transaction/TransactionValidationException.java @@ -1,6 +1,6 @@ package com.limechain.exception.transaction; -public class TransactionValidationException extends RuntimeException { +public class TransactionValidationException extends Exception { public TransactionValidationException(String message) { super(message); } diff --git a/src/main/java/com/limechain/network/Network.java b/src/main/java/com/limechain/network/Network.java index 859389a7d..be388b714 100644 --- a/src/main/java/com/limechain/network/Network.java +++ b/src/main/java/com/limechain/network/Network.java @@ -21,10 +21,8 @@ import com.limechain.network.protocol.transaction.TransactionsService; import com.limechain.network.protocol.warp.WarpSyncService; import com.limechain.network.protocol.warp.dto.WarpSyncResponse; -import com.limechain.rpc.server.AppBean; import com.limechain.storage.DBConstants; import com.limechain.storage.KVRepository; -import com.limechain.sync.warpsync.WarpSyncState; import com.limechain.utils.Ed25519Utils; import com.limechain.utils.StringUtils; import io.ipfs.multiaddr.MultiAddress; @@ -352,34 +350,27 @@ private boolean isPeerInvalid() { return false; } - public void handshakeBootNodes() { + public void blockAnnounceHandshakeBootNodes() { kademliaService.getBootNodePeerIds() .stream() .distinct() - .forEach(this::sendGrandpaHandshake); + .forEach(p -> new Thread(() -> blockAnnounceService.sendHandshake(this.host, p)).start()); } - private void sendGrandpaHandshake(PeerId peerId) { - //TODO: - // when using threads we connect to more than 10 peers, but have some unhandled exceptions, - // without them we connect to only 2 peers - new Thread(() -> - blockAnnounceService.sendHandshake(this.host, this.host.getAddressBook(), peerId) - ).start(); + public void handshakePeers() { + connectionManager.getPeerIds().forEach(peerId -> new Thread(() -> { + blockAnnounceService.sendHandshake(this.host, peerId); + grandpaService.sendHandshake(this.host, peerId); + transactionsService.sendHandshake(this.host, peerId); + }).start() + ); } @Scheduled(fixedRate = 5, initialDelay = 5, timeUnit = TimeUnit.MINUTES) - public void sendNeighbourMessages() { - if (!AppBean.getBean(WarpSyncState.class).isWarpSyncFinished()) { - return; - } + public void sendMessagesToPeers() { connectionManager.getPeerIds().forEach(peerId -> grandpaService.sendNeighbourMessage(this.host, peerId)); connectionManager.getPeerIds().forEach(peerId -> transactionsService.sendTransactionsMessage(this.host, peerId)); } - - public void sendNeighbourMessage(PeerId peerId) { - grandpaService.sendNeighbourMessage(this.host, peerId); - } } diff --git a/src/main/java/com/limechain/network/protocol/blockannounce/BlockAnnounceService.java b/src/main/java/com/limechain/network/protocol/blockannounce/BlockAnnounceService.java index de3d8221b..d14e0b485 100644 --- a/src/main/java/com/limechain/network/protocol/blockannounce/BlockAnnounceService.java +++ b/src/main/java/com/limechain/network/protocol/blockannounce/BlockAnnounceService.java @@ -1,7 +1,6 @@ package com.limechain.network.protocol.blockannounce; import com.limechain.network.protocol.NetworkService; -import io.libp2p.core.AddressBook; import io.libp2p.core.Host; import io.libp2p.core.PeerId; import lombok.extern.java.Log; @@ -12,13 +11,12 @@ public BlockAnnounceService(String protocolId) { this.protocol = new BlockAnnounce(protocolId, new BlockAnnounceProtocol()); } - public void sendHandshake(Host us, AddressBook addrs, PeerId peer) { - try{ - BlockAnnounceController controller = this.protocol.dialPeer(us, peer, addrs); + public void sendHandshake(Host us, PeerId peer) { + try { + BlockAnnounceController controller = this.protocol.dialPeer(us, peer, us.getAddressBook()); controller.sendHandshake(); - } catch (IllegalStateException e){ + } catch (IllegalStateException e) { log.warning("Error sending handshake request to peer " + peer); } - } } diff --git a/src/main/java/com/limechain/network/protocol/grandpa/GrandpaService.java b/src/main/java/com/limechain/network/protocol/grandpa/GrandpaService.java index 3254d75f1..21c63766b 100644 --- a/src/main/java/com/limechain/network/protocol/grandpa/GrandpaService.java +++ b/src/main/java/com/limechain/network/protocol/grandpa/GrandpaService.java @@ -15,6 +15,7 @@ @Log public class GrandpaService extends NetworkService { ConnectionManager connectionManager = ConnectionManager.getInstance(); + public GrandpaService(String protocolId) { this.protocol = new Grandpa(protocolId, new GrandpaProtocol()); } @@ -23,7 +24,7 @@ public GrandpaService(String protocolId) { * Sends a neighbour message to a peer. If there is no initiator stream opened with the peer, * sends a handshake instead. * - * @param us our host object + * @param us our host object * @param peerId message receiver */ public void sendNeighbourMessage(Host us, PeerId peerId) { @@ -40,8 +41,8 @@ private void sendNeighbourMessage(Stream stream) { controller.sendNeighbourMessage(); } - private void sendHandshake(Host us, PeerId peerId) { - try{ + public void sendHandshake(Host us, PeerId peerId) { + try { GrandpaController controller = this.protocol.dialPeer(us, peerId, us.getAddressBook()); controller.sendHandshake(); } catch (Exception e) { diff --git a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java index 1c20493bb..c7d919736 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java @@ -35,12 +35,12 @@ public class TransactionsEngine { private final ConnectionManager connectionManager; private final TransactionState transactionState; - private final BlockState blockState; + private final TransactionValidator transactionValidator; public TransactionsEngine() { connectionManager = ConnectionManager.getInstance(); transactionState = AppBean.getBean(TransactionState.class); - blockState = BlockState.getInstance(); + transactionValidator = AppBean.getBean(TransactionValidator.class); } /** @@ -84,7 +84,7 @@ private void handleInitiatorStreamMessage(byte[] message, Stream stream) { connectionManager.addTransactionsStream(stream); log.log(Level.INFO, "Received transactions handshake from " + peerId); stream.writeAndFlush(new byte[]{}); - //TODO *2nd* Send valid transactions to the peer we received a handshake from + //TODO Send valid transactions to the peer we received a handshake from } private void handleResponderStreamMessage(byte[] message, Stream stream) { @@ -123,30 +123,14 @@ private void handleTransactionMessage(byte[] message, PeerId peerId) { for (int i = 0; i < transactions.getExtrinsics().length; i++) { Extrinsic current = transactions.getExtrinsics()[i]; - if (transactionState.existsInQueue(current) || transactionState.existsInPool(current)) { - continue; - } - - final BlockHeader header = blockState.bestBlockHeader(); - if (header == null) { - log.log(Level.WARNING, "No best block header found"); - return; - } - - final Runtime runtime = blockState.getRuntime(header.getHash()); - if (runtime == null) { - log.log(Level.WARNING, "No runtime found for block header " + header.getHash()); - return; - } ValidTransaction validTransaction; try { - validTransaction = TransactionValidator.validateTransaction(runtime, header.getHash(), current); + validTransaction = transactionValidator.validateTransactions(current); validTransaction.getIgnore().add(peerId); } catch (TransactionValidationException e) { - log.fine("Error when validating transaction " + current.toString() + log.warning("Error when validating transaction " + current.toString() + " from protocol: " + e.getMessage()); - continue; } diff --git a/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java index 6eb6184cf..2f0a116c1 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsService.java @@ -38,7 +38,7 @@ private void sendTransactions(Stream stream) { //TODO Send transaction messages } - private void sendHandshake(Host us, PeerId peerId) { + public void sendHandshake(Host us, PeerId peerId) { try { TransactionsController controller = this.protocol.dialPeer(us, peerId, us.getAddressBook()); controller.sendHandshake(); diff --git a/src/main/java/com/limechain/sync/fullsync/FullSyncMachine.java b/src/main/java/com/limechain/sync/fullsync/FullSyncMachine.java index ede5fec0c..b8aa7a234 100644 --- a/src/main/java/com/limechain/sync/fullsync/FullSyncMachine.java +++ b/src/main/java/com/limechain/sync/fullsync/FullSyncMachine.java @@ -22,7 +22,6 @@ import com.limechain.storage.block.SyncState; import com.limechain.storage.trie.TrieStorage; import com.limechain.sync.fullsync.inherents.InherentData; -import com.limechain.transaction.TransactionValidator; import com.limechain.transaction.dto.Extrinsic; import com.limechain.trie.DiskTrieAccessor; import com.limechain.trie.TrieAccessor; @@ -30,7 +29,6 @@ import com.limechain.trie.structure.TrieStructure; import com.limechain.trie.structure.database.NodeData; import com.limechain.trie.structure.nibble.Nibbles; -import com.limechain.utils.StringUtils; import com.limechain.utils.scale.ScaleUtils; import com.limechain.utils.scale.readers.PairReader; import io.emeraldpay.polkaj.scale.ScaleCodecReader; @@ -81,6 +79,8 @@ public void start() { loadStateAtBlockFromPeer(lastFinalizedBlockHash); } + networkService.blockAnnounceHandshakeBootNodes(); + runtime = buildRuntimeFromState(trieAccessor); StateVersion runtimeStateVersion = runtime.getVersion().getStateVersion(); BabeApiConfiguration babeApiConfiguration = runtime.callBabeApiConfiguration(); @@ -111,6 +111,7 @@ public void start() { } blockState.setFullSyncFinished(true); + networkService.handshakePeers(); } private TrieStructure loadStateAtBlockFromPeer(Hash256 lastFinalizedBlockHash) { diff --git a/src/main/java/com/limechain/sync/warpsync/WarpSyncMachine.java b/src/main/java/com/limechain/sync/warpsync/WarpSyncMachine.java index a241b1aa5..607e55eb1 100644 --- a/src/main/java/com/limechain/sync/warpsync/WarpSyncMachine.java +++ b/src/main/java/com/limechain/sync/warpsync/WarpSyncMachine.java @@ -100,7 +100,6 @@ public void stop() { private void finishWarpSync() { this.warpState.setWarpSyncFinished(true); - this.networkService.handshakeBootNodes(); this.syncState.persistState(); BlockState.getInstance().initializeAfterWarpSync( diff --git a/src/main/java/com/limechain/sync/warpsync/WarpSyncState.java b/src/main/java/com/limechain/sync/warpsync/WarpSyncState.java index 1297b5756..839e10212 100644 --- a/src/main/java/com/limechain/sync/warpsync/WarpSyncState.java +++ b/src/main/java/com/limechain/sync/warpsync/WarpSyncState.java @@ -149,7 +149,7 @@ private void updateState(CommitMessage commitMessage) { } syncState.finalizedCommitMessage(commitMessage); - log.log(Level.INFO, "Reached block #" + lastFinalizedBlockNumber); + log.log(Level.INFO, "Reached block #" + syncState.getLastFinalizedBlockNumber()); if (warpSyncFinished && scheduledRuntimeUpdateBlocks.contains(lastFinalizedBlockNumber)) { new Thread(this::updateRuntime).start(); } @@ -257,7 +257,6 @@ public void loadSavedRuntimeCode() { * @param peerId sender of message */ public void syncNeighbourMessage(NeighbourMessage neighbourMessage, PeerId peerId) { - network.sendNeighbourMessage(peerId); if (warpSyncFinished && neighbourMessage.getSetId().compareTo(syncState.getSetId()) > 0) { updateSetData(neighbourMessage.getLastFinalizedBlock().add(BigInteger.ONE), peerId); } @@ -305,7 +304,7 @@ public void handleScheduledEvents() { } if (warpSyncFinished && updated) { log.log(Level.INFO, "Successfully transitioned to authority set id: " + setId); - new Thread(network::sendNeighbourMessages).start(); + new Thread(network::sendMessagesToPeers).start(); } } diff --git a/src/main/java/com/limechain/transaction/TransactionValidator.java b/src/main/java/com/limechain/transaction/TransactionValidator.java index 8c894b571..c71181c5b 100644 --- a/src/main/java/com/limechain/transaction/TransactionValidator.java +++ b/src/main/java/com/limechain/transaction/TransactionValidator.java @@ -2,9 +2,11 @@ import com.limechain.exception.misc.RuntimeApiVersionException; import com.limechain.exception.transaction.TransactionValidationException; +import com.limechain.network.protocol.warp.dto.BlockHeader; import com.limechain.runtime.Runtime; import com.limechain.runtime.RuntimeEndpoint; import com.limechain.runtime.version.ApiVersionName; +import com.limechain.storage.block.BlockState; import com.limechain.transaction.dto.Extrinsic; import com.limechain.transaction.dto.TransactionSource; import com.limechain.transaction.dto.TransactionValidationRequest; @@ -14,29 +16,58 @@ import com.limechain.utils.scale.readers.TransactionValidationReader; import com.limechain.utils.scale.writers.TransactionValidationWriter; import io.emeraldpay.polkaj.types.Hash256; -import lombok.extern.java.Log; +import org.springframework.stereotype.Component; import java.math.BigInteger; import java.util.Objects; -@Log +@Component public class TransactionValidator { - public static ValidTransaction validateTransaction(Runtime runtime, Hash256 hash, Extrinsic transaction) { - byte[] scaleRequest = createValidationRequest(runtime, hash, transaction); - byte[] validationResult = runtime.call( - RuntimeEndpoint.TRANSACTION_QUEUE_VALIDATE_TRANSACTION, scaleRequest); - TransactionValidationResponse response = - ScaleUtils.Decode.decode(validationResult, new TransactionValidationReader()); + private final TransactionState transactionState; + private final BlockState blockState; + + public TransactionValidator(TransactionState transactionState) { + this.transactionState = transactionState; + this.blockState = BlockState.getInstance(); + } + + public synchronized ValidTransaction validateTransactions(Extrinsic extrinsic) + throws TransactionValidationException { + if (transactionState.existsInQueue(extrinsic) || transactionState.existsInPool(extrinsic)) { + throw new TransactionValidationException("Transaction already validated."); + } + + final BlockHeader header = blockState.bestBlockHeader(); + if (header == null) { + throw new TransactionValidationException("No best block header found while validating."); + } + + final Runtime runtime = blockState.getRuntime(header.getHash()); + if (runtime == null) { + throw new TransactionValidationException("No runtime found for block header " + header.getHash() + + " while validating."); + } + + TransactionValidationResponse response = validateTransactionInner(runtime, header.getHash(), extrinsic); if (!Objects.isNull(response.getTransactionValidityError())) { throw new TransactionValidationException(response.getTransactionValidityError().toString()); } - return new ValidTransaction(transaction, response.getValidTx()); + return new ValidTransaction(extrinsic, response.getValidTx()); + } + + private static TransactionValidationResponse validateTransactionInner(Runtime runtime, + Hash256 hash, + Extrinsic transaction) { + byte[] scaleRequest = createScaleValidationRequest(runtime, hash, transaction); + byte[] validationResult = runtime.call( + RuntimeEndpoint.TRANSACTION_QUEUE_VALIDATE_TRANSACTION, scaleRequest); + return ScaleUtils.Decode.decode(validationResult, new TransactionValidationReader()); } - private static byte[] createValidationRequest(Runtime runtime, Hash256 hash256, Extrinsic transaction) { + private static byte[] createScaleValidationRequest(Runtime runtime, Hash256 hash256, Extrinsic transaction) { BigInteger txQueueVersion = runtime.getVersion().getApis() .getApiVersion(ApiVersionName.TRANSACTION_QUEUE_API.getHashedName()); @@ -53,8 +84,7 @@ private static byte[] createValidationRequest(Runtime runtime, Hash256 hash256, request.setTransaction(transaction.getData()); request.setParentBlockHash(hash256); } - default -> throw new RuntimeApiVersionException( - String.format("Invalid transaction queue version: %d", txQueueVersion)); + default -> throw new RuntimeApiVersionException("Invalid transaction queue version: " + txQueueVersion); } return ScaleUtils.Encode.encode(new TransactionValidationWriter(), request); diff --git a/src/main/java/com/limechain/utils/scale/ScaleUtils.java b/src/main/java/com/limechain/utils/scale/ScaleUtils.java index 6639a41d4..091417e52 100644 --- a/src/main/java/com/limechain/utils/scale/ScaleUtils.java +++ b/src/main/java/com/limechain/utils/scale/ScaleUtils.java @@ -28,6 +28,17 @@ @UtilityClass public class ScaleUtils { + /** + * A utility method that returns true if the scale decoded result is successful. See + * Results section. + * + * @param reader a reader with preloaded byte data. + * @return true if result byte is 0, false otherwise. + */ + public boolean isScaleResultSuccessful(ScaleCodecReader reader) { + return reader.readUByte() == 0; + } + @UtilityClass public class Decode { @@ -51,9 +62,9 @@ public T decode(byte[] encodedData, ScaleReader reader) { /** * Decodes a byte array representing a list of items into a List using the provided ScaleReader for the list items. * - * @param encodedData The byte array containing the encoded list. + * @param encodedData The byte array containing the encoded list. * @param listItemReader The ScaleReader implementation for decoding individual list items. - * @param The type of objects in the list. + * @param The type of objects in the list. * @return The decoded List of items. * @throws ScaleDecodingException If an error occurs during decoding. */ @@ -68,25 +79,25 @@ public class Encode { /** * Encodes a list of pairs into SCALE format using the provided serializers for the key and value types. * - * @param pairs The list of pairs to encode. + * @param pairs The list of pairs to encode. * @param fstSerializer The serializer function for the first element of each pair. * @param sndSerializer The serializer function for the second element of each pair. - * @param The type of the first element in the pairs. - * @param The type of the second element in the pairs. + * @param The type of the first element in the pairs. + * @param The type of the second element in the pairs. * @return The encoded byte array representing the list of pairs. */ public byte[] encodeListOfPairs( - List> pairs, - Function fstSerializer, - Function sndSerializer + List> pairs, + Function fstSerializer, + Function sndSerializer ) { return encodeListOfPairs( - pairs.stream() - .map(p -> - new Pair<>( - fstSerializer.apply(p.getValue0()), - sndSerializer.apply(p.getValue1()))) - .toList()); + pairs.stream() + .map(p -> + new Pair<>( + fstSerializer.apply(p.getValue0()), + sndSerializer.apply(p.getValue1()))) + .toList()); } /** @@ -97,11 +108,11 @@ public byte[] encodeListOfPairs( */ public byte[] encodeListOfPairs(List> pairs) { return encode( - new ListWriter<>( - new PairWriter<>( - ScaleCodecWriter::writeAsList, - ScaleCodecWriter::writeAsList)), - pairs); + new ListWriter<>( + new PairWriter<>( + ScaleCodecWriter::writeAsList, + ScaleCodecWriter::writeAsList)), + pairs); } /** @@ -159,16 +170,17 @@ public byte[] encode(ScaleWriter writer, T value) { * Scale encodes a nullable value as an optional. * If the value is null, it is encoded as an empty optional. * If the value is not null, it is encoded as an optional with a present value. + * * @param writer The ScaleWriter for encoding the value, if not null. - * @param value The nullable object to encode. - * @return The encoded optional value. + * @param value The nullable object to encode. * @param + * @return The encoded optional value. * @throws ScaleEncodingException If an unexpected error occurs during encoding. */ public byte[] encodeOptional(ScaleWriter writer, @Nullable T value) { return ScaleUtils.Encode.encode( - (scaleCodecWriter, val) -> scaleCodecWriter.writeOptional(writer, val), - value + (scaleCodecWriter, val) -> scaleCodecWriter.writeOptional(writer, val), + value ); } } diff --git a/src/main/java/com/limechain/utils/scale/readers/TransactionValidationReader.java b/src/main/java/com/limechain/utils/scale/readers/TransactionValidationReader.java index 6099e8256..e4abf13fe 100644 --- a/src/main/java/com/limechain/utils/scale/readers/TransactionValidationReader.java +++ b/src/main/java/com/limechain/utils/scale/readers/TransactionValidationReader.java @@ -5,12 +5,11 @@ import com.limechain.transaction.dto.TransactionValidity; import com.limechain.transaction.dto.TransactionValidityError; import com.limechain.transaction.dto.UnknownTransactionType; +import com.limechain.utils.scale.ScaleUtils; import io.emeraldpay.polkaj.scale.ScaleCodecReader; import io.emeraldpay.polkaj.scale.ScaleReader; import io.emeraldpay.polkaj.scale.reader.UInt64Reader; -import java.util.Objects; - public class TransactionValidationReader implements ScaleReader { private static final int INVALID_TRANSACTION_TYPE = 0; @@ -19,7 +18,7 @@ public class TransactionValidationReader implements ScaleReader Date: Mon, 21 Oct 2024 17:29:24 +0300 Subject: [PATCH 19/19] chore: Address some of the comments. --- .../network/protocol/transaction/TransactionsEngine.java | 1 + .../java/com/limechain/transaction/TransactionPool.java | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java index c7d919736..444e98e49 100644 --- a/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java +++ b/src/main/java/com/limechain/network/protocol/transaction/TransactionsEngine.java @@ -163,6 +163,7 @@ public void writeHandshakeToStream(Stream stream, PeerId peerId) { */ public void writeTransactionsMessage(Stream stream, PeerId peerId) { ByteArrayOutputStream buf = new ByteArrayOutputStream(); + //TODO Replace empty transaction messages once we have vlaidation working. try (ScaleCodecWriter writer = new ScaleCodecWriter(buf)) { writer.write(new TransactionsWriter(), new ExtrinsicArray(new Extrinsic[]{ new Extrinsic(new byte[]{}), new Extrinsic(new byte[]{}) diff --git a/src/main/java/com/limechain/transaction/TransactionPool.java b/src/main/java/com/limechain/transaction/TransactionPool.java index a054749a9..3d3922266 100644 --- a/src/main/java/com/limechain/transaction/TransactionPool.java +++ b/src/main/java/com/limechain/transaction/TransactionPool.java @@ -9,7 +9,11 @@ public class TransactionPool { - private final Map transactions = new HashMap<>(); + private final Map transactions; + + public TransactionPool() { + transactions = new HashMap<>(); + } public ValidTransaction get(Extrinsic extrinsic) { byte[] key = HashUtils.hashWithBlake2b(extrinsic.getData());