mirror of https://github.com/qortal/qortal
Browse Source
Arbitrary transactions now [de]serialize data-type (raw/hash) for v4+ transactions. Data type also stored in repository. Very small (<=255 byte) data payloads are also stored directly in HSQLDB. Added ArbitraryDataManager which looks for hash-only data payloads and possibly requests raw data over network depending on 'policy' (which currently is "fetch everything"). Added networking support for finding, and transferring, arbitrary data payloads. Minor optimization to message ID generation in Peer. Minor optimization in Serialization.serializeSizedString()pull/67/head
catbref
5 years ago
14 changed files with 513 additions and 48 deletions
@ -0,0 +1,91 @@
|
||||
package org.qora.controller; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.Random; |
||||
|
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
import org.qora.api.resource.TransactionsResource.ConfirmationStatus; |
||||
import org.qora.data.transaction.ArbitraryTransactionData; |
||||
import org.qora.data.transaction.TransactionData; |
||||
import org.qora.repository.DataException; |
||||
import org.qora.repository.Repository; |
||||
import org.qora.repository.RepositoryManager; |
||||
import org.qora.transaction.ArbitraryTransaction; |
||||
import org.qora.transaction.Transaction.TransactionType; |
||||
|
||||
public class ArbitraryDataManager extends Thread { |
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(ArbitraryDataManager.class); |
||||
private static final List<TransactionType> ARBITRARY_TX_TYPE = Arrays.asList(TransactionType.ARBITRARY); |
||||
|
||||
private static ArbitraryDataManager instance; |
||||
|
||||
private volatile boolean isStopping = false; |
||||
|
||||
private ArbitraryDataManager() { |
||||
} |
||||
|
||||
public static ArbitraryDataManager getInstance() { |
||||
if (instance == null) |
||||
instance = new ArbitraryDataManager(); |
||||
|
||||
return instance; |
||||
} |
||||
|
||||
@Override |
||||
public void run() { |
||||
Thread.currentThread().setName("Arbitrary Data Manager"); |
||||
|
||||
try { |
||||
while (!isStopping) { |
||||
Thread.sleep(2000); |
||||
|
||||
// Any arbitrary transactions we want to fetch data for?
|
||||
try (final Repository repository = RepositoryManager.getRepository()) { |
||||
List<byte[]> signatures = repository.getTransactionRepository().getSignaturesMatchingCriteria(null, null, null, ARBITRARY_TX_TYPE, null, null, ConfirmationStatus.BOTH, null, null, true); |
||||
if (signatures == null || signatures.isEmpty()) |
||||
continue; |
||||
|
||||
// Filter out those that already have local data
|
||||
signatures.removeIf(signature -> hasLocalData(repository, signature)); |
||||
|
||||
if (signatures.isEmpty()) |
||||
continue; |
||||
|
||||
// Pick one at random
|
||||
final int index = new Random().nextInt(signatures.size()); |
||||
byte[] signature = signatures.get(index); |
||||
|
||||
Controller.getInstance().fetchArbitraryData(signature); |
||||
} catch (DataException e) { |
||||
LOGGER.error("Repository issue when fetching arbitrary transaction data", e); |
||||
} |
||||
} |
||||
} catch (InterruptedException e) { |
||||
return; |
||||
} |
||||
} |
||||
|
||||
public void shutdown() { |
||||
isStopping = true; |
||||
this.interrupt(); |
||||
} |
||||
|
||||
private boolean hasLocalData(final Repository repository, final byte[] signature) { |
||||
try { |
||||
TransactionData transactionData = repository.getTransactionRepository().fromSignature(signature); |
||||
if (transactionData == null || !(transactionData instanceof ArbitraryTransactionData)) |
||||
return true; |
||||
|
||||
ArbitraryTransaction arbitraryTransaction = new ArbitraryTransaction(repository, transactionData); |
||||
|
||||
return arbitraryTransaction.isDataLocal(); |
||||
} catch (DataException e) { |
||||
LOGGER.error("Repository issue when checking arbitrary transaction's data is local", e); |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,73 @@
|
||||
package org.qora.network.message; |
||||
|
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.IOException; |
||||
import java.io.UnsupportedEncodingException; |
||||
import java.nio.ByteBuffer; |
||||
|
||||
import org.qora.transform.Transformer; |
||||
|
||||
import com.google.common.primitives.Ints; |
||||
|
||||
public class ArbitraryDataMessage extends Message { |
||||
|
||||
private static final int SIGNATURE_LENGTH = Transformer.SIGNATURE_LENGTH; |
||||
|
||||
private byte[] signature; |
||||
private byte[] data; |
||||
|
||||
public ArbitraryDataMessage(byte[] signature, byte[] data) { |
||||
this(-1, signature, data); |
||||
} |
||||
|
||||
private ArbitraryDataMessage(int id, byte[] signature, byte[] data) { |
||||
super(id, MessageType.ARBITRARY_DATA); |
||||
|
||||
this.signature = signature; |
||||
this.data = data; |
||||
} |
||||
|
||||
public byte[] getSignature() { |
||||
return this.signature; |
||||
} |
||||
|
||||
public byte[] getData() { |
||||
return this.data; |
||||
} |
||||
|
||||
public static Message fromByteBuffer(int id, ByteBuffer byteBuffer) throws UnsupportedEncodingException { |
||||
byte[] signature = new byte[SIGNATURE_LENGTH]; |
||||
byteBuffer.get(signature); |
||||
|
||||
int dataLength = byteBuffer.getInt(); |
||||
|
||||
if (byteBuffer.remaining() != dataLength) |
||||
return null; |
||||
|
||||
byte[] data = new byte[dataLength]; |
||||
byteBuffer.get(data); |
||||
|
||||
return new ArbitraryDataMessage(id, signature, data); |
||||
} |
||||
|
||||
@Override |
||||
protected byte[] toData() { |
||||
if (this.data == null) |
||||
return null; |
||||
|
||||
try { |
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream(); |
||||
|
||||
bytes.write(this.signature); |
||||
|
||||
bytes.write(Ints.toByteArray(this.data.length)); |
||||
|
||||
bytes.write(this.data); |
||||
|
||||
return bytes.toByteArray(); |
||||
} catch (IOException e) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,54 @@
|
||||
package org.qora.network.message; |
||||
|
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.IOException; |
||||
import java.io.UnsupportedEncodingException; |
||||
import java.nio.ByteBuffer; |
||||
|
||||
import org.qora.transform.Transformer; |
||||
|
||||
public class GetArbitraryDataMessage extends Message { |
||||
|
||||
private static final int SIGNATURE_LENGTH = Transformer.SIGNATURE_LENGTH; |
||||
|
||||
private byte[] signature; |
||||
|
||||
public GetArbitraryDataMessage(byte[] signature) { |
||||
this(-1, signature); |
||||
} |
||||
|
||||
private GetArbitraryDataMessage(int id, byte[] signature) { |
||||
super(id, MessageType.GET_ARBITRARY_DATA); |
||||
|
||||
this.signature = signature; |
||||
} |
||||
|
||||
public byte[] getSignature() { |
||||
return this.signature; |
||||
} |
||||
|
||||
public static Message fromByteBuffer(int id, ByteBuffer bytes) throws UnsupportedEncodingException { |
||||
if (bytes.remaining() != SIGNATURE_LENGTH) |
||||
return null; |
||||
|
||||
byte[] signature = new byte[SIGNATURE_LENGTH]; |
||||
|
||||
bytes.get(signature); |
||||
|
||||
return new GetArbitraryDataMessage(id, signature); |
||||
} |
||||
|
||||
@Override |
||||
protected byte[] toData() { |
||||
try { |
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream(); |
||||
|
||||
bytes.write(this.signature); |
||||
|
||||
return bytes.toByteArray(); |
||||
} catch (IOException e) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue