diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index 07c7db6f..55c13b36 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -366,14 +366,9 @@ public class Block { long timestamp = calcTimestamp(parentBlockData, minter.getPublicKey(), minterLevel); long onlineAccountsTimestamp = OnlineAccountsManager.getCurrentOnlineAccountTimestamp(); - // Fetch our list of online accounts + // Fetch our list of online accounts, removing any that are missing a nonce List onlineAccounts = OnlineAccountsManager.getInstance().getOnlineAccounts(onlineAccountsTimestamp); - - // If mempow is active, remove any legacy accounts that are missing a nonce - if (timestamp >= BlockChain.getInstance().getOnlineAccountsMemoryPoWTimestamp()) { - onlineAccounts.removeIf(a -> a.getNonce() == null || a.getNonce() < 0); - } - + onlineAccounts.removeIf(a -> a.getNonce() == null || a.getNonce() < 0); if (onlineAccounts.isEmpty()) { LOGGER.debug("No online accounts - not even our own?"); return null; @@ -412,29 +407,27 @@ public class Block { // Aggregated, single signature byte[] onlineAccountsSignatures = Qortal25519Extras.aggregateSignatures(signaturesToAggregate); - // Add nonces to the end of the online accounts signatures if mempow is active - if (timestamp >= BlockChain.getInstance().getOnlineAccountsMemoryPoWTimestamp()) { - try { - // Create ordered list of nonce values - List nonces = new ArrayList<>(); - for (int i = 0; i < onlineAccountsCount; ++i) { - Integer accountIndex = accountIndexes.get(i); - OnlineAccountData onlineAccountData = indexedOnlineAccounts.get(accountIndex); - nonces.add(onlineAccountData.getNonce()); - } - - // Encode the nonces to a byte array - byte[] encodedNonces = BlockTransformer.encodeOnlineAccountNonces(nonces); - - // Append the encoded nonces to the encoded online account signatures - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - outputStream.write(onlineAccountsSignatures); - outputStream.write(encodedNonces); - onlineAccountsSignatures = outputStream.toByteArray(); - } - catch (TransformationException | IOException e) { - return null; + // Add nonces to the end of the online accounts signatures + try { + // Create ordered list of nonce values + List nonces = new ArrayList<>(); + for (int i = 0; i < onlineAccountsCount; ++i) { + Integer accountIndex = accountIndexes.get(i); + OnlineAccountData onlineAccountData = indexedOnlineAccounts.get(accountIndex); + nonces.add(onlineAccountData.getNonce()); } + + // Encode the nonces to a byte array + byte[] encodedNonces = BlockTransformer.encodeOnlineAccountNonces(nonces); + + // Append the encoded nonces to the encoded online account signatures + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + outputStream.write(onlineAccountsSignatures); + outputStream.write(encodedNonces); + onlineAccountsSignatures = outputStream.toByteArray(); + } + catch (TransformationException | IOException e) { + return null; } byte[] minterSignature = minter.sign(BlockTransformer.getBytesForMinterSignature(parentBlockData, @@ -1047,14 +1040,9 @@ public class Block { final int signaturesLength = Transformer.SIGNATURE_LENGTH; final int noncesLength = onlineRewardShares.size() * Transformer.INT_LENGTH; - if (this.blockData.getTimestamp() >= BlockChain.getInstance().getOnlineAccountsMemoryPoWTimestamp()) { - // We expect nonces to be appended to the online accounts signatures - if (this.blockData.getOnlineAccountsSignatures().length != signaturesLength + noncesLength) - return ValidationResult.ONLINE_ACCOUNT_SIGNATURES_MALFORMED; - } else { - if (this.blockData.getOnlineAccountsSignatures().length != signaturesLength) - return ValidationResult.ONLINE_ACCOUNT_SIGNATURES_MALFORMED; - } + // We expect nonces to be appended to the online accounts signatures + if (this.blockData.getOnlineAccountsSignatures().length != signaturesLength + noncesLength) + return ValidationResult.ONLINE_ACCOUNT_SIGNATURES_MALFORMED; // Check signatures long onlineTimestamp = this.blockData.getOnlineAccountsTimestamp(); @@ -1063,32 +1051,30 @@ public class Block { byte[] encodedOnlineAccountSignatures = this.blockData.getOnlineAccountsSignatures(); // Split online account signatures into signature(s) + nonces, then validate the nonces - if (this.blockData.getTimestamp() >= BlockChain.getInstance().getOnlineAccountsMemoryPoWTimestamp()) { - byte[] extractedSignatures = BlockTransformer.extract(encodedOnlineAccountSignatures, 0, signaturesLength); - byte[] extractedNonces = BlockTransformer.extract(encodedOnlineAccountSignatures, signaturesLength, onlineRewardShares.size() * Transformer.INT_LENGTH); - encodedOnlineAccountSignatures = extractedSignatures; + byte[] extractedSignatures = BlockTransformer.extract(encodedOnlineAccountSignatures, 0, signaturesLength); + byte[] extractedNonces = BlockTransformer.extract(encodedOnlineAccountSignatures, signaturesLength, onlineRewardShares.size() * Transformer.INT_LENGTH); + encodedOnlineAccountSignatures = extractedSignatures; - List nonces = BlockTransformer.decodeOnlineAccountNonces(extractedNonces); + List nonces = BlockTransformer.decodeOnlineAccountNonces(extractedNonces); - // Build block's view of online accounts (without signatures, as we don't need them here) - Set onlineAccounts = new HashSet<>(); - for (int i = 0; i < onlineRewardShares.size(); ++i) { - Integer nonce = nonces.get(i); - byte[] publicKey = onlineRewardShares.get(i).getRewardSharePublicKey(); + // Build block's view of online accounts (without signatures, as we don't need them here) + Set onlineAccounts = new HashSet<>(); + for (int i = 0; i < onlineRewardShares.size(); ++i) { + Integer nonce = nonces.get(i); + byte[] publicKey = onlineRewardShares.get(i).getRewardSharePublicKey(); - OnlineAccountData onlineAccountData = new OnlineAccountData(onlineTimestamp, null, publicKey, nonce); - 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 (!OnlineAccountsManager.getInstance().verifyMemoryPoW(onlineAccount, this.blockData.getTimestamp())) - return ValidationResult.ONLINE_ACCOUNT_NONCE_INCORRECT; + OnlineAccountData onlineAccountData = new OnlineAccountData(onlineTimestamp, null, publicKey, nonce); + 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 (!OnlineAccountsManager.getInstance().verifyMemoryPoW(onlineAccount, this.blockData.getTimestamp())) + return ValidationResult.ONLINE_ACCOUNT_NONCE_INCORRECT; + // Extract online accounts' timestamp signatures from block data. Only one signature if aggregated. List onlineAccountsSignatures = BlockTransformer.decodeTimestampSignatures(encodedOnlineAccountSignatures); diff --git a/src/main/java/org/qortal/block/BlockChain.java b/src/main/java/org/qortal/block/BlockChain.java index 42692a18..826fdd78 100644 --- a/src/main/java/org/qortal/block/BlockChain.java +++ b/src/main/java/org/qortal/block/BlockChain.java @@ -195,10 +195,6 @@ public class BlockChain { * featureTriggers because unit tests need to set this value via Reflection. */ private long onlineAccountsModulusV2Timestamp; - /** Feature trigger timestamp for online accounts mempow verification. Can't use featureTriggers - * because unit tests need to set this value via Reflection. */ - private long onlineAccountsMemoryPoWTimestamp; - /** Max reward shares by block height */ public static class MaxRewardSharesByTimestamp { public long timestamp; @@ -359,10 +355,6 @@ public class BlockChain { return this.onlineAccountsModulusV2Timestamp; } - public long getOnlineAccountsMemoryPoWTimestamp() { - return this.onlineAccountsMemoryPoWTimestamp; - } - /** Returns true if approval-needing transaction types require a txGroupId other than NO_GROUP. */ public boolean getRequireGroupForApproval() { return this.requireGroupForApproval; diff --git a/src/main/java/org/qortal/controller/OnlineAccountsManager.java b/src/main/java/org/qortal/controller/OnlineAccountsManager.java index 45b47f5d..0e24bdfc 100644 --- a/src/main/java/org/qortal/controller/OnlineAccountsManager.java +++ b/src/main/java/org/qortal/controller/OnlineAccountsManager.java @@ -20,7 +20,6 @@ import org.qortal.network.message.*; import org.qortal.repository.DataException; import org.qortal.repository.Repository; import org.qortal.repository.RepositoryManager; -import org.qortal.settings.Settings; import org.qortal.utils.Base58; import org.qortal.utils.NTP; import org.qortal.utils.NamedThreadFactory; @@ -156,7 +155,6 @@ public class OnlineAccountsManager { return; byte[] timestampBytes = Longs.toByteArray(onlineAccountsTimestamp); - final boolean mempowActive = onlineAccountsTimestamp >= BlockChain.getInstance().getOnlineAccountsMemoryPoWTimestamp(); Set replacementAccounts = new HashSet<>(); for (PrivateKeyAccount onlineAccount : onlineAccounts) { @@ -165,7 +163,7 @@ public class OnlineAccountsManager { byte[] signature = Qortal25519Extras.signForAggregation(onlineAccount.getPrivateKey(), timestampBytes); byte[] publicKey = onlineAccount.getPublicKey(); - Integer nonce = mempowActive ? new Random().nextInt(500000) : null; + Integer nonce = new Random().nextInt(500000); OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey, nonce); replacementAccounts.add(ourOnlineAccountData); @@ -321,13 +319,10 @@ public class OnlineAccountsManager { return false; } - // Validate mempow if feature trigger is active (or if online account's timestamp is past the trigger timestamp) - long memoryPoWStartTimestamp = BlockChain.getInstance().getOnlineAccountsMemoryPoWTimestamp(); - if (now >= memoryPoWStartTimestamp || onlineAccountTimestamp >= memoryPoWStartTimestamp) { - if (!getInstance().verifyMemoryPoW(onlineAccountData, now)) { - LOGGER.trace(() -> String.format("Rejecting online reward-share for account %s due to invalid PoW nonce", mintingAccount.getAddress())); - return false; - } + // Validate mempow + if (!getInstance().verifyMemoryPoW(onlineAccountData, now)) { + LOGGER.trace(() -> String.format("Rejecting online reward-share for account %s due to invalid PoW nonce", mintingAccount.getAddress())); + return false; } return true; @@ -471,12 +466,10 @@ public class OnlineAccountsManager { // 'next' timestamp (prioritize this as it's the most important, if mempow active) final long nextOnlineAccountsTimestamp = toOnlineAccountTimestamp(now) + getOnlineTimestampModulus(); - if (isMemoryPoWActive(now)) { - boolean success = computeOurAccountsForTimestamp(nextOnlineAccountsTimestamp); - if (!success) { - // We didn't compute the required nonce value(s), and so can't proceed until they have been retried - return; - } + boolean success = computeOurAccountsForTimestamp(nextOnlineAccountsTimestamp); + if (!success) { + // We didn't compute the required nonce value(s), and so can't proceed until they have been retried + return; } // 'current' timestamp @@ -553,21 +546,15 @@ public class OnlineAccountsManager { // Compute nonce Integer nonce; - if (isMemoryPoWActive(NTP.getTime())) { - try { - nonce = this.computeMemoryPoW(mempowBytes, publicKey, onlineAccountsTimestamp); - if (nonce == null) { - // A nonce is required - return false; - } - } catch (TimeoutException e) { - LOGGER.info(String.format("Timed out computing nonce for account %.8s", Base58.encode(publicKey))); + try { + nonce = this.computeMemoryPoW(mempowBytes, publicKey, onlineAccountsTimestamp); + if (nonce == null) { + // A nonce is required return false; } - } - else { - // Send -1 if we haven't computed a nonce due to feature trigger timestamp - nonce = -1; + } catch (TimeoutException e) { + LOGGER.info(String.format("Timed out computing nonce for account %.8s", Base58.encode(publicKey))); + return false; } byte[] signature = Qortal25519Extras.signForAggregation(privateKey, timestampBytes); @@ -599,12 +586,6 @@ public class OnlineAccountsManager { // MemoryPoW - private boolean isMemoryPoWActive(Long timestamp) { - if (timestamp >= BlockChain.getInstance().getOnlineAccountsMemoryPoWTimestamp()) { - return true; - } - return false; - } private byte[] getMemoryPoWBytes(byte[] publicKey, long onlineAccountsTimestamp) throws IOException { byte[] timestampBytes = Longs.toByteArray(onlineAccountsTimestamp); @@ -616,11 +597,6 @@ public class OnlineAccountsManager { } private Integer computeMemoryPoW(byte[] bytes, byte[] publicKey, long onlineAccountsTimestamp) throws TimeoutException { - if (!isMemoryPoWActive(NTP.getTime())) { - LOGGER.info("Mempow start timestamp not yet reached"); - return null; - } - LOGGER.info(String.format("Computing nonce for account %.8s and timestamp %d...", Base58.encode(publicKey), onlineAccountsTimestamp)); // Calculate the time until the next online timestamp and use it as a timeout when computing the nonce @@ -643,12 +619,6 @@ public class OnlineAccountsManager { } public boolean verifyMemoryPoW(OnlineAccountData onlineAccountData, Long timestamp) { - long memoryPoWStartTimestamp = BlockChain.getInstance().getOnlineAccountsMemoryPoWTimestamp(); - if (timestamp < memoryPoWStartTimestamp && onlineAccountData.getTimestamp() < memoryPoWStartTimestamp) { - // Not active yet, so treat it as valid - return true; - } - // Require a valid nonce value if (onlineAccountData.getNonce() == null || onlineAccountData.getNonce() < 0) { return false; diff --git a/src/main/java/org/qortal/transform/block/BlockTransformer.java b/src/main/java/org/qortal/transform/block/BlockTransformer.java index 9e02a6f5..c97aa090 100644 --- a/src/main/java/org/qortal/transform/block/BlockTransformer.java +++ b/src/main/java/org/qortal/transform/block/BlockTransformer.java @@ -235,7 +235,7 @@ public class BlockTransformer extends Transformer { // Online accounts timestamp is only present if there are also signatures onlineAccountsTimestamp = byteBuffer.getLong(); - final int signaturesByteLength = getOnlineAccountSignaturesLength(onlineAccountsSignaturesCount, onlineAccountsCount, timestamp); + final int signaturesByteLength = (onlineAccountsSignaturesCount * Transformer.SIGNATURE_LENGTH) + (onlineAccountsCount * INT_LENGTH); if (signaturesByteLength > BlockChain.getInstance().getMaxBlockSize()) throw new TransformationException("Byte data too long for online accounts signatures"); @@ -511,16 +511,6 @@ public class BlockTransformer extends Transformer { return nonces; } - public static int getOnlineAccountSignaturesLength(int onlineAccountsSignaturesCount, int onlineAccountCount, long blockTimestamp) { - if (blockTimestamp >= BlockChain.getInstance().getOnlineAccountsMemoryPoWTimestamp()) { - // Once mempow is active, we expect the online account signatures to be appended with the nonce values - return (onlineAccountsSignaturesCount * Transformer.SIGNATURE_LENGTH) + (onlineAccountCount * INT_LENGTH); - } - else { - // Before mempow, only the online account signatures were included (which will likely be a single signature) - return onlineAccountsSignaturesCount * Transformer.SIGNATURE_LENGTH; - } - } public static byte[] extract(byte[] input, int pos, int length) { byte[] output = new byte[length]; diff --git a/src/main/resources/blockchain.json b/src/main/resources/blockchain.json index e9f1500d..893add5e 100644 --- a/src/main/resources/blockchain.json +++ b/src/main/resources/blockchain.json @@ -24,7 +24,6 @@ "onlineAccountSignaturesMinLifetime": 43200000, "onlineAccountSignaturesMaxLifetime": 86400000, "onlineAccountsModulusV2Timestamp": 1659801600000, - "onlineAccountsMemoryPoWTimestamp": 1666454400000, "rewardsByHeight": [ { "height": 1, "reward": 5.00 }, { "height": 259201, "reward": 4.75 }, diff --git a/src/test/java/org/qortal/test/common/AccountUtils.java b/src/test/java/org/qortal/test/common/AccountUtils.java index 0d0b6d6a..0d8baae2 100644 --- a/src/test/java/org/qortal/test/common/AccountUtils.java +++ b/src/test/java/org/qortal/test/common/AccountUtils.java @@ -124,8 +124,6 @@ public class AccountUtils { long timestamp = System.currentTimeMillis(); byte[] timestampBytes = Longs.toByteArray(timestamp); - final boolean mempowActive = timestamp >= BlockChain.getInstance().getOnlineAccountsMemoryPoWTimestamp(); - for (int a = 0; a < numAccounts; ++a) { byte[] privateKey = new byte[Transformer.PUBLIC_KEY_LENGTH]; SECURE_RANDOM.nextBytes(privateKey); @@ -135,7 +133,7 @@ public class AccountUtils { byte[] signature = signForAggregation(privateKey, timestampBytes); - Integer nonce = mempowActive ? new Random().nextInt(500000) : null; + Integer nonce = new Random().nextInt(500000); onlineAccounts.add(new OnlineAccountData(timestamp, signature, publicKey, nonce)); }