forked from Qortal/qortal
WIP: use blockchain feature-trigger "aggregateSignatureTimestamp" to determine when online-accounts sigs and block sigs switch to aggregate sigs
This commit is contained in:
parent
51930d3ccf
commit
84d850ee0b
@ -27,6 +27,7 @@ import org.qortal.block.BlockChain.BlockTimingByHeight;
|
|||||||
import org.qortal.block.BlockChain.AccountLevelShareBin;
|
import org.qortal.block.BlockChain.AccountLevelShareBin;
|
||||||
import org.qortal.controller.OnlineAccountsManager;
|
import org.qortal.controller.OnlineAccountsManager;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
|
import org.qortal.crypto.Qortal25519Extras;
|
||||||
import org.qortal.data.account.AccountBalanceData;
|
import org.qortal.data.account.AccountBalanceData;
|
||||||
import org.qortal.data.account.AccountData;
|
import org.qortal.data.account.AccountData;
|
||||||
import org.qortal.data.account.EligibleQoraHolderData;
|
import org.qortal.data.account.EligibleQoraHolderData;
|
||||||
@ -388,12 +389,24 @@ public class Block {
|
|||||||
byte[] encodedOnlineAccounts = BlockTransformer.encodeOnlineAccounts(onlineAccountsSet);
|
byte[] encodedOnlineAccounts = BlockTransformer.encodeOnlineAccounts(onlineAccountsSet);
|
||||||
int onlineAccountsCount = onlineAccountsSet.size();
|
int onlineAccountsCount = onlineAccountsSet.size();
|
||||||
|
|
||||||
// Concatenate online account timestamp signatures (in correct order)
|
byte[] onlineAccountsSignatures;
|
||||||
byte[] onlineAccountsSignatures = new byte[onlineAccountsCount * Transformer.SIGNATURE_LENGTH];
|
if (timestamp >= BlockChain.getInstance().getAggregateSignatureTimestamp()) {
|
||||||
for (int i = 0; i < onlineAccountsCount; ++i) {
|
// Collate all signatures
|
||||||
Integer accountIndex = accountIndexes.get(i);
|
Collection<byte[]> signaturesToAggregate = indexedOnlineAccounts.values()
|
||||||
OnlineAccountData onlineAccountData = indexedOnlineAccounts.get(accountIndex);
|
.stream()
|
||||||
System.arraycopy(onlineAccountData.getSignature(), 0, onlineAccountsSignatures, i * Transformer.SIGNATURE_LENGTH, Transformer.SIGNATURE_LENGTH);
|
.map(OnlineAccountData::getSignature)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// Aggregated, single signature
|
||||||
|
onlineAccountsSignatures = Qortal25519Extras.aggregateSignatures(signaturesToAggregate);
|
||||||
|
} else {
|
||||||
|
// Concatenate online account timestamp signatures (in correct order)
|
||||||
|
onlineAccountsSignatures = new byte[onlineAccountsCount * Transformer.SIGNATURE_LENGTH];
|
||||||
|
for (int i = 0; i < onlineAccountsCount; ++i) {
|
||||||
|
Integer accountIndex = accountIndexes.get(i);
|
||||||
|
OnlineAccountData onlineAccountData = indexedOnlineAccounts.get(accountIndex);
|
||||||
|
System.arraycopy(onlineAccountData.getSignature(), 0, onlineAccountsSignatures, i * Transformer.SIGNATURE_LENGTH, Transformer.SIGNATURE_LENGTH);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] minterSignature = minter.sign(BlockTransformer.getBytesForMinterSignature(parentBlockData,
|
byte[] minterSignature = minter.sign(BlockTransformer.getBytesForMinterSignature(parentBlockData,
|
||||||
@ -1003,36 +1016,57 @@ public class Block {
|
|||||||
if (this.blockData.getOnlineAccountsSignatures() == null || this.blockData.getOnlineAccountsSignatures().length == 0)
|
if (this.blockData.getOnlineAccountsSignatures() == null || this.blockData.getOnlineAccountsSignatures().length == 0)
|
||||||
return ValidationResult.ONLINE_ACCOUNT_SIGNATURES_MISSING;
|
return ValidationResult.ONLINE_ACCOUNT_SIGNATURES_MISSING;
|
||||||
|
|
||||||
if (this.blockData.getOnlineAccountsSignatures().length != onlineRewardShares.size() * Transformer.SIGNATURE_LENGTH)
|
if (this.blockData.getTimestamp() >= BlockChain.getInstance().getAggregateSignatureTimestamp()) {
|
||||||
return ValidationResult.ONLINE_ACCOUNT_SIGNATURES_MALFORMED;
|
// We expect just the one, aggregated signature
|
||||||
|
if (this.blockData.getOnlineAccountsSignatures().length != Transformer.SIGNATURE_LENGTH)
|
||||||
|
return ValidationResult.ONLINE_ACCOUNT_SIGNATURES_MALFORMED;
|
||||||
|
} else {
|
||||||
|
if (this.blockData.getOnlineAccountsSignatures().length != onlineRewardShares.size() * Transformer.SIGNATURE_LENGTH)
|
||||||
|
return ValidationResult.ONLINE_ACCOUNT_SIGNATURES_MALFORMED;
|
||||||
|
}
|
||||||
|
|
||||||
// Check signatures
|
// Check signatures
|
||||||
long onlineTimestamp = this.blockData.getOnlineAccountsTimestamp();
|
long onlineTimestamp = this.blockData.getOnlineAccountsTimestamp();
|
||||||
byte[] onlineTimestampBytes = Longs.toByteArray(onlineTimestamp);
|
byte[] onlineTimestampBytes = Longs.toByteArray(onlineTimestamp);
|
||||||
|
|
||||||
// Extract online accounts' timestamp signatures from block data
|
// Extract online accounts' timestamp signatures from block data. Only one signature if aggregated.
|
||||||
List<byte[]> onlineAccountsSignatures = BlockTransformer.decodeTimestampSignatures(this.blockData.getOnlineAccountsSignatures());
|
List<byte[]> onlineAccountsSignatures = BlockTransformer.decodeTimestampSignatures(this.blockData.getOnlineAccountsSignatures());
|
||||||
|
|
||||||
// Convert
|
if (this.blockData.getTimestamp() >= BlockChain.getInstance().getAggregateSignatureTimestamp()) {
|
||||||
Set<OnlineAccountData> onlineAccounts = new HashSet<>();
|
// Aggregate all public keys
|
||||||
for (int i = 0; i < onlineAccountsSignatures.size(); ++i) {
|
Collection<byte[]> publicKeys = onlineRewardShares.stream()
|
||||||
byte[] signature = onlineAccountsSignatures.get(i);
|
.map(RewardShareData::getRewardSharePublicKey)
|
||||||
byte[] publicKey = onlineRewardShares.get(i).getRewardSharePublicKey();
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
OnlineAccountData onlineAccountData = new OnlineAccountData(onlineTimestamp, signature, publicKey);
|
byte[] aggregatePublicKey = Qortal25519Extras.aggregatePublicKeys(publicKeys);
|
||||||
onlineAccounts.add(onlineAccountData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove those already validated & cached by online accounts manager - no need to re-validate them
|
byte[] aggregateSignature = onlineAccountsSignatures.get(0);
|
||||||
OnlineAccountsManager.getInstance().removeKnown(onlineAccounts, onlineTimestamp);
|
|
||||||
|
|
||||||
// Validate the rest
|
// One-step verification of aggregate signature using aggregate public key
|
||||||
for (OnlineAccountData onlineAccount : onlineAccounts)
|
if (!Qortal25519Extras.verifyAggregated(aggregatePublicKey, aggregateSignature, onlineTimestampBytes))
|
||||||
if (!Crypto.verify(onlineAccount.getPublicKey(), onlineAccount.getSignature(), onlineTimestampBytes))
|
|
||||||
return ValidationResult.ONLINE_ACCOUNT_SIGNATURE_INCORRECT;
|
return ValidationResult.ONLINE_ACCOUNT_SIGNATURE_INCORRECT;
|
||||||
|
} else {
|
||||||
|
// Build block's view of online accounts
|
||||||
|
Set<OnlineAccountData> onlineAccounts = new HashSet<>();
|
||||||
|
for (int i = 0; i < onlineAccountsSignatures.size(); ++i) {
|
||||||
|
byte[] signature = onlineAccountsSignatures.get(i);
|
||||||
|
byte[] publicKey = onlineRewardShares.get(i).getRewardSharePublicKey();
|
||||||
|
|
||||||
// We've validated these, so allow online accounts manager to cache
|
OnlineAccountData onlineAccountData = new OnlineAccountData(onlineTimestamp, signature, publicKey);
|
||||||
OnlineAccountsManager.getInstance().addBlocksOnlineAccounts(onlineAccounts, onlineTimestamp);
|
onlineAccounts.add(onlineAccountData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove those already validated & cached by online accounts manager - no need to re-validate them
|
||||||
|
OnlineAccountsManager.getInstance().removeKnown(onlineAccounts, onlineTimestamp);
|
||||||
|
|
||||||
|
// Validate the rest
|
||||||
|
for (OnlineAccountData onlineAccount : onlineAccounts)
|
||||||
|
if (!Crypto.verify(onlineAccount.getPublicKey(), onlineAccount.getSignature(), onlineTimestampBytes))
|
||||||
|
return ValidationResult.ONLINE_ACCOUNT_SIGNATURE_INCORRECT;
|
||||||
|
|
||||||
|
// We've validated these, so allow online accounts manager to cache
|
||||||
|
OnlineAccountsManager.getInstance().addBlocksOnlineAccounts(onlineAccounts, onlineTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
// All online accounts valid, so save our list of online accounts for potential later use
|
// All online accounts valid, so save our list of online accounts for potential later use
|
||||||
this.cachedOnlineRewardShares = onlineRewardShares;
|
this.cachedOnlineRewardShares = onlineRewardShares;
|
||||||
|
@ -70,7 +70,8 @@ public class BlockChain {
|
|||||||
shareBinFix,
|
shareBinFix,
|
||||||
calcChainWeightTimestamp,
|
calcChainWeightTimestamp,
|
||||||
transactionV5Timestamp,
|
transactionV5Timestamp,
|
||||||
transactionV6Timestamp;
|
transactionV6Timestamp,
|
||||||
|
aggregateSignatureTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom transaction fees
|
// Custom transaction fees
|
||||||
@ -410,6 +411,10 @@ public class BlockChain {
|
|||||||
return this.featureTriggers.get(FeatureTrigger.transactionV6Timestamp.name()).longValue();
|
return this.featureTriggers.get(FeatureTrigger.transactionV6Timestamp.name()).longValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getAggregateSignatureTimestamp() {
|
||||||
|
return this.featureTriggers.get(FeatureTrigger.aggregateSignatureTimestamp.name()).longValue();
|
||||||
|
}
|
||||||
|
|
||||||
// More complex getters for aspects that change by height or timestamp
|
// More complex getters for aspects that change by height or timestamp
|
||||||
|
|
||||||
public long getRewardAtHeight(int ourHeight) {
|
public long getRewardAtHeight(int ourHeight) {
|
||||||
|
@ -9,6 +9,7 @@ import org.qortal.account.PrivateKeyAccount;
|
|||||||
import org.qortal.block.Block;
|
import org.qortal.block.Block;
|
||||||
import org.qortal.block.BlockChain;
|
import org.qortal.block.BlockChain;
|
||||||
import org.qortal.crypto.Crypto;
|
import org.qortal.crypto.Crypto;
|
||||||
|
import org.qortal.crypto.Qortal25519Extras;
|
||||||
import org.qortal.data.account.MintingAccountData;
|
import org.qortal.data.account.MintingAccountData;
|
||||||
import org.qortal.data.account.RewardShareData;
|
import org.qortal.data.account.RewardShareData;
|
||||||
import org.qortal.data.network.OnlineAccountData;
|
import org.qortal.data.network.OnlineAccountData;
|
||||||
@ -204,7 +205,10 @@ public class OnlineAccountsManager {
|
|||||||
|
|
||||||
// Verify signature
|
// Verify signature
|
||||||
byte[] data = Longs.toByteArray(onlineAccountData.getTimestamp());
|
byte[] data = Longs.toByteArray(onlineAccountData.getTimestamp());
|
||||||
if (!Crypto.verify(rewardSharePublicKey, onlineAccountData.getSignature(), data)) {
|
boolean isSignatureValid = onlineAccountTimestamp >= BlockChain.getInstance().getAggregateSignatureTimestamp()
|
||||||
|
? Qortal25519Extras.verifyAggregated(rewardSharePublicKey, onlineAccountData.getSignature(), data)
|
||||||
|
: Crypto.verify(rewardSharePublicKey, onlineAccountData.getSignature(), data);
|
||||||
|
if (!isSignatureValid) {
|
||||||
LOGGER.trace(() -> String.format("Rejecting invalid online account %s", Base58.encode(rewardSharePublicKey)));
|
LOGGER.trace(() -> String.format("Rejecting invalid online account %s", Base58.encode(rewardSharePublicKey)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -387,14 +391,18 @@ public class OnlineAccountsManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final boolean useAggregateCompatibleSignature = onlineAccountsTimestamp >= BlockChain.getInstance().getAggregateSignatureTimestamp();
|
||||||
|
|
||||||
byte[] timestampBytes = Longs.toByteArray(onlineAccountsTimestamp);
|
byte[] timestampBytes = Longs.toByteArray(onlineAccountsTimestamp);
|
||||||
List<OnlineAccountData> ourOnlineAccounts = new ArrayList<>();
|
List<OnlineAccountData> ourOnlineAccounts = new ArrayList<>();
|
||||||
|
|
||||||
for (MintingAccountData mintingAccountData : mintingAccounts) {
|
for (MintingAccountData mintingAccountData : mintingAccounts) {
|
||||||
PrivateKeyAccount mintingAccount = new PrivateKeyAccount(null, mintingAccountData.getPrivateKey());
|
byte[] privateKey = mintingAccountData.getPrivateKey();
|
||||||
|
byte[] publicKey = Crypto.toPublicKey(privateKey);
|
||||||
|
|
||||||
byte[] signature = mintingAccount.sign(timestampBytes);
|
byte[] signature = useAggregateCompatibleSignature
|
||||||
byte[] publicKey = mintingAccount.getPublicKey();
|
? Qortal25519Extras.signForAggregation(privateKey, timestampBytes)
|
||||||
|
: Crypto.sign(privateKey, timestampBytes);
|
||||||
|
|
||||||
// Our account is online
|
// Our account is online
|
||||||
OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey);
|
OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey);
|
||||||
|
@ -59,7 +59,8 @@
|
|||||||
"shareBinFix": 399000,
|
"shareBinFix": 399000,
|
||||||
"calcChainWeightTimestamp": 1620579600000,
|
"calcChainWeightTimestamp": 1620579600000,
|
||||||
"transactionV5Timestamp": 1642176000000,
|
"transactionV5Timestamp": 1642176000000,
|
||||||
"transactionV6Timestamp": 9999999999999
|
"transactionV6Timestamp": 9999999999999,
|
||||||
|
"aggregateSignatureTimestamp": 9999999999999
|
||||||
},
|
},
|
||||||
"genesisInfo": {
|
"genesisInfo": {
|
||||||
"version": 4,
|
"version": 4,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user