Skip to content

Commit

Permalink
Submit nonce improvements (#183)
Browse files Browse the repository at this point in the history
* Closes #92: Don't transmit passphrase when solo mining if correctly configured

* Accept RS or numeric accountId field

* Don't log passphrases

* Add option to disallow others from mining through your node
  • Loading branch information
harryjph committed Jun 20, 2019
1 parent 8d6a8cc commit b9ad678
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 36 deletions.
14 changes: 13 additions & 1 deletion conf/brs-default.properties
Expand Up @@ -313,4 +313,16 @@ IndirectIncomingService.Enable = true
AutoPopOff.Enable = true

# List of CORS allowed origins.
API.AllowedOrigins=*
API.AllowedOrigins=*

# List of semicolon-separated passphrases to use when solo mining. When mining solo, if you enter your passphrase here,
# you can set your miner to pool mining mode and avoid sending your passphrase over the wire constantly.
# Do not use on public facing nodes or nodes that are accessible (filesystem or API server) by others, as it could
# cause your passphrase to become compromised or allow others to mine on your behalf without your knowledge
SoloMiningPassphrases=passphrase1;passphrase2;passphrase3;

# Allow anyone to use the "submitNonce" API call. This call can be abused to force your node to perform lots
# of work in order to effectively mine for others. Enabling this option will only allow accounts whose passphrases
# are in SoloMiningPassphrases to mine through this node. It is highly recommended that you restrict this but at the
# moment it is not restricted to ensure smooth upgrades.
AllowOtherSoloMiners=true
1 change: 0 additions & 1 deletion src/brs/BlockchainProcessorImpl.java
Expand Up @@ -1254,7 +1254,6 @@ public void generateBlock(String secretPhrase, byte[] publicKey, Long nonce) thr
previousBlock.getId(), totalAmountNQT, totalFeeNQT, Burst.getFluxCapacitor().getValue(FluxValues.MAX_PAYLOAD_LENGTH) - payloadSize, payloadHash, publicKey,
generationSignature, null, previousBlockHash, new ArrayList<>(orderedBlockTransactions), nonce,
byteATs, previousBlock.getHeight());

} catch (BurstException.ValidationException e) {
// shouldn't happen because all transactions are already validated
logger.info("Error generating block", e);
Expand Down
48 changes: 21 additions & 27 deletions src/brs/GeneratorImpl.java
Expand Up @@ -26,42 +26,36 @@ public class GeneratorImpl implements Generator {
private final Listeners<GeneratorState, Event> listeners = new Listeners<>();
private final ConcurrentMap<Long, GeneratorStateImpl> generators = new ConcurrentHashMap<>();
private final Blockchain blockchain;
private final TimeService timeService;
private final FluxCapacitor fluxCapacitor;

public GeneratorImpl(Blockchain blockchain, TimeService timeService, FluxCapacitor fluxCapacitor) {
this.blockchain = blockchain;
this.timeService = timeService;
this.fluxCapacitor = fluxCapacitor;
}

private Runnable generateBlockThread(BlockchainProcessor blockchainProcessor) {
return () -> {
if (blockchainProcessor.isScanning()) {
return;
}
try {
if (blockchainProcessor.isScanning()) {
return;
}
try {
long currentBlock = blockchain.getLastBlock().getHeight();
Iterator<Entry<Long, GeneratorStateImpl>> it = generators.entrySet().iterator();
while (it.hasNext() && !Thread.currentThread().isInterrupted() && ThreadPool.running.get()) {
Entry<Long, GeneratorStateImpl> generator = it.next();
if (currentBlock < generator.getValue().getBlock()) {
generator.getValue().forge(blockchainProcessor);
} else {
it.remove();
}
long currentBlock = blockchain.getLastBlock().getHeight();
Iterator<Entry<Long, GeneratorStateImpl>> it = generators.entrySet().iterator();
while (it.hasNext() && !Thread.currentThread().isInterrupted() && ThreadPool.running.get()) {
Entry<Long, GeneratorStateImpl> generator = it.next();
if (currentBlock < generator.getValue().getBlock()) {
generator.getValue().forge(blockchainProcessor);
} else {
it.remove();
}
} catch (BlockchainProcessor.BlockNotAcceptedException e) {
logger.debug("Error in block generation thread", e);
}
} catch (Exception t) {
logger.info("CRITICAL ERROR. PLEASE REPORT TO THE DEVELOPERS.\n" + t.toString(), t);
System.exit(1);
} catch (BlockchainProcessor.BlockNotAcceptedException e) {
logger.debug("Error in block generation thread", e);
}

};
}
private final TimeService timeService;
private final FluxCapacitor fluxCapacitor;

public GeneratorImpl(Blockchain blockchain, TimeService timeService, FluxCapacitor fluxCapacitor) {
this.blockchain = blockchain;
this.timeService = timeService;
this.fluxCapacitor = fluxCapacitor;
}

@Override
public void generateForBlockchainProcessor(ThreadPool threadPool, BlockchainProcessor blockchainProcessor) {
Expand Down
2 changes: 1 addition & 1 deletion src/brs/http/APIServlet.java
Expand Up @@ -132,7 +132,7 @@ public APIServlet(TransactionProcessor transactionProcessor, Blockchain blockcha
map.put("signTransaction", new SignTransaction(parameterService, transactionService));
map.put("transferAsset", new TransferAsset(parameterService, blockchain, apiTransactionManager, accountService));
map.put("getMiningInfo", new GetMiningInfo(blockchain, generator));
map.put("submitNonce", new SubmitNonce(accountService, blockchain, generator));
map.put("submitNonce", new SubmitNonce(propertyService, accountService, blockchain, generator));
map.put("getRewardRecipient", new GetRewardRecipient(parameterService, blockchain, accountService));
map.put("setRewardRecipient", new SetRewardRecipient(parameterService, blockchain, accountService, apiTransactionManager));
map.put("getAccountsWithRewardRecipient", new GetAccountsWithRewardRecipient(parameterService, accountService));
Expand Down
32 changes: 30 additions & 2 deletions src/brs/http/SubmitNonce.java
Expand Up @@ -6,24 +6,36 @@
import brs.crypto.Crypto;
import brs.grpc.handlers.SubmitNonceHandler;
import brs.grpc.proto.ApiException;
import brs.props.PropertyService;
import brs.props.Props;
import brs.services.AccountService;
import brs.util.Convert;
import burst.kit.crypto.BurstCrypto;
import burst.kit.entity.BurstAddress;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static brs.http.common.Parameters.*;


final class SubmitNonce extends APIServlet.JsonRequestHandler {

private final Map<Long, String> passphrases;
private final AccountService accountService;
private final Blockchain blockchain;
private final Generator generator;

SubmitNonce(AccountService accountService, Blockchain blockchain, Generator generator) {
SubmitNonce(PropertyService propertyService, AccountService accountService, Blockchain blockchain, Generator generator) {
super(new APITag[] {APITag.MINING}, SECRET_PHRASE_PARAMETER, NONCE_PARAMETER, ACCOUNT_ID_PARAMETER, BLOCK_HEIGHT_PARAMETER);
BurstCrypto burstCrypto = BurstCrypto.getInstance();
this.passphrases = propertyService.getStringList(Props.SOLO_MINING_PASSPHRASES)
.stream()
.collect(Collectors.toMap(passphrase -> burstCrypto.getBurstAddressFromPassphrase(passphrase).getBurstID().getSignedLongId(), Function.identity()));

this.accountService = accountService;
this.blockchain = blockchain;
Expand Down Expand Up @@ -55,7 +67,23 @@ JsonElement processRequest(HttpServletRequest req) {
}

if(secret == null) {
response.addProperty("result", "Missing Passphrase");
long accountIdLong;
try {
accountIdLong = BurstAddress.fromEither(accountId).getBurstID().getSignedLongId();
} catch (Exception e) {
response.addProperty("result", "Missing Passphrase and Account ID is malformed");
return response;
}
if (passphrases.containsKey(accountIdLong)) {
secret = passphrases.get(accountIdLong);
} else {
response.addProperty("result", "Missing Passphrase and account passphrase not in solo mining config");
return response;
}
}

if (!passphrases.containsValue(secret)) {
response.addProperty("result", "This account is not allowed to mine on this node as the whitelist is enabled and it is not whitelisted.");
return response;
}

Expand Down
6 changes: 2 additions & 4 deletions src/brs/props/PropertyServiceImpl.java
Expand Up @@ -4,10 +4,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.*;

public class PropertyServiceImpl implements PropertyService {

Expand Down Expand Up @@ -100,6 +97,7 @@ public List<String> getStringList(Prop<String> name) {
}

private void logOnce(String propertyName, boolean debugLevel, String logText, Object... arguments) {
if (Objects.equals(propertyName, Props.SOLO_MINING_PASSPHRASES.getName())) return;
if (!this.alreadyLoggedProperties.contains(propertyName)) {
if (debugLevel) {
this.logger.debug(logText, arguments);
Expand Down
3 changes: 3 additions & 0 deletions src/brs/props/Props.java
Expand Up @@ -166,6 +166,9 @@ public class Props {

public static final Prop<Boolean> ENABLE_AT_DEBUG_LOG = new Prop<>("ATDebugLog.Enable", false);

public static final Prop<String> SOLO_MINING_PASSPHRASES = new Prop<>("SoloMiningPassphrases", "");
public static final Prop<Boolean> ALLOW_OTHER_SOLO_MINERS = new Prop<>("AllowOtherSoloMiners", false);

private Props() { //no need to construct
}
}

0 comments on commit b9ad678

Please sign in to comment.