diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index 483090bc..680ef3b5 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -916,26 +916,34 @@ public class Block { expandedAccounts.add(rewardShareData); } - // Possibly check signatures if block is recent + // If block is past a certain age then we simply assume the signatures were correct long signatureRequirementThreshold = NTP.getTime() - BlockChain.getInstance().getOnlineAccountSignaturesMinLifetime(); - if (this.blockData.getTimestamp() >= signatureRequirementThreshold) { - if (this.blockData.getOnlineAccountsSignatures() == null || this.blockData.getOnlineAccountsSignatures().length == 0) - return ValidationResult.ONLINE_ACCOUNT_SIGNATURES_MISSING; + if (this.blockData.getTimestamp() < signatureRequirementThreshold) + return ValidationResult.OK; - if (this.blockData.getOnlineAccountsSignatures().length != expandedAccounts.size() * Transformer.SIGNATURE_LENGTH) - return ValidationResult.ONLINE_ACCOUNT_SIGNATURES_MALFORMED; + if (this.blockData.getOnlineAccountsSignatures() == null || this.blockData.getOnlineAccountsSignatures().length == 0) + return ValidationResult.ONLINE_ACCOUNT_SIGNATURES_MISSING; - // Check signatures - List onlineAccountsSignatures = BlockTransformer.decodeTimestampSignatures(this.blockData.getOnlineAccountsSignatures()); - byte[] message = Longs.toByteArray(this.blockData.getOnlineAccountsTimestamp()); + if (this.blockData.getOnlineAccountsSignatures().length != expandedAccounts.size() * Transformer.SIGNATURE_LENGTH) + return ValidationResult.ONLINE_ACCOUNT_SIGNATURES_MALFORMED; - for (int i = 0; i < onlineAccountsSignatures.size(); ++i) { - PublicKeyAccount account = new PublicKeyAccount(null, expandedAccounts.get(i).getRewardSharePublicKey()); - byte[] signature = onlineAccountsSignatures.get(i); + // Check signatures + List onlineAccountsSignatures = BlockTransformer.decodeTimestampSignatures(this.blockData.getOnlineAccountsSignatures()); + long onlineTimestamp = this.blockData.getOnlineAccountsTimestamp(); + byte[] onlineTimestampBytes = Longs.toByteArray(onlineTimestamp); + List onlineAccounts = Controller.getInstance().getOnlineAccounts(); - if (!account.verify(signature, message)) - return ValidationResult.ONLINE_ACCOUNT_SIGNATURE_INCORRECT; - } + for (int i = 0; i < onlineAccountsSignatures.size(); ++i) { + byte[] signature = onlineAccountsSignatures.get(i); + byte[] publicKey = expandedAccounts.get(i).getRewardSharePublicKey(); + + // If signature is still current then no need to perform Ed25519 verify + OnlineAccountData onlineAccountData = new OnlineAccountData(onlineTimestamp, signature, publicKey); + if (onlineAccounts.remove(onlineAccountData)) // remove() is like contains() but also reduces the number to check next time + continue; + + if (!PublicKeyAccount.verify(publicKey, signature, onlineTimestampBytes)) + return ValidationResult.ONLINE_ACCOUNT_SIGNATURE_INCORRECT; } return ValidationResult.OK; diff --git a/src/main/java/org/qortal/data/network/OnlineAccountData.java b/src/main/java/org/qortal/data/network/OnlineAccountData.java index 04ae92f9..15792307 100644 --- a/src/main/java/org/qortal/data/network/OnlineAccountData.java +++ b/src/main/java/org/qortal/data/network/OnlineAccountData.java @@ -1,5 +1,7 @@ package org.qortal.data.network; +import java.util.Arrays; + import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; @@ -44,4 +46,36 @@ public class OnlineAccountData { return new PublicKeyAccount(null, this.publicKey).getAddress(); } + // Comparison + + @Override + public boolean equals(Object other) { + if (other == this) + return true; + + if (!(other instanceof OnlineAccountData)) + return false; + + OnlineAccountData otherOnlineAccountData = (OnlineAccountData) other; + + // Very quick comparison + if (otherOnlineAccountData.timestamp != this.timestamp) + return false; + + // Signature more likely to be unique than public key + if (!Arrays.equals(otherOnlineAccountData.signature, this.signature)) + return false; + + if (!Arrays.equals(otherOnlineAccountData.publicKey, this.publicKey)) + return false; + + return true; + } + + @Override + public int hashCode() { + // Pretty lazy implementation + return (int) this.timestamp; + } + }