package co.nyzo.verifier.scripts; import co.nyzo.verifier.*; import co.nyzo.verifier.client.ClientTransactionUtil; import co.nyzo.verifier.client.CommandOutputConsole; import co.nyzo.verifier.client.ConsoleColor; import co.nyzo.verifier.messages.BootstrapRequest; import co.nyzo.verifier.messages.BootstrapResponseV2; import co.nyzo.verifier.messages.MeshResponse; import co.nyzo.verifier.nyzoString.NyzoString; import co.nyzo.verifier.nyzoString.NyzoStringEncoder; import co.nyzo.verifier.nyzoString.NyzoStringSignature; import co.nyzo.verifier.sentinel.ManagedVerifier; import co.nyzo.verifier.sentinel.Sentinel; import co.nyzo.verifier.sentinel.SentinelUtil; import co.nyzo.verifier.util.*; import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; public class CycleTransactionSignScript { public static void main(String[] args) { signTransactions(args); UpdateUtil.terminate(); } private static void signTransactions(String[] args) { // Check the length of the argument array. Return if insufficient arguments are provided. if (args.length < 2) { LogUtil.println("\n\n\n"); LogUtil.println("***********************************************************************"); LogUtil.println("arguments:"); LogUtil.println("- Nyzo string signature of the cycle transaction you want to sign"); LogUtil.println("- 1 to vote for the transaction, 0 to vote against the transaction"); LogUtil.println("***********************************************************************\n\n\n"); return; } // Get the signature. Return if it is not valid. NyzoString decodedSignature = NyzoStringEncoder.decode(args[0]); if (!(decodedSignature instanceof NyzoStringSignature)) { LogUtil.println(ConsoleColor.Red.backgroundBright() + "not a valid Nyzo string signature: " + args[0] + ConsoleColor.reset); return; } // Get the vote. Return if it is not valid. String voteString = args[1]; if (!voteString.equals("1") && !voteString.equals("0")) { LogUtil.println(ConsoleColor.Red.backgroundBright() + "vote is invalid, must be 1 or 0: " + args[0] + ConsoleColor.reset); return; } byte vote = voteString.equals("1") ? (byte) 1 : (byte) 0; // Get the managed verifiers. Return if none are set. List<ManagedVerifier> managedVerifiers = Sentinel.getManagedVerifiers(); if (managedVerifiers.isEmpty()) { LogUtil.println(ConsoleColor.Red.backgroundBright() + "no managed verifiers specified in " + Sentinel.managedVerifiersFile + ConsoleColor.reset); return; } // Initialize the frozen edge. This is necessary for proper operation of CycleTransactionManager. SentinelUtil.initializeFrozenEdge(managedVerifiers); // Get the transaction. Return if unable to find it. Transaction transaction = getTransaction(((NyzoStringSignature) decodedSignature).getSignature()); if (transaction == null) { LogUtil.println(ConsoleColor.Red.backgroundBright() + "unable to find specified transaction" + ConsoleColor.reset); return; } // Fetch the cycle nodes. Return if empty. Set<Node> cycleNodes = fetchCycleNodes(managedVerifiers); if (cycleNodes.isEmpty()) { LogUtil.println(ConsoleColor.Red.backgroundBright() + "unable to fetch cycle nodes" + ConsoleColor.reset); return; } // Fetch the cycle order. List<ByteBuffer> cycleOrder = fetchCycleOrder(managedVerifiers); if (cycleOrder.isEmpty()) { LogUtil.println(ConsoleColor.Red.backgroundBright() + "unable to fetch cycle order" + ConsoleColor.reset); return; } // Send the signatures. sendSignatures(transaction.getSignature(), vote, cycleNodes, cycleOrder, managedVerifiers); } private static Transaction getTransaction(byte[] signature) { // Try to get the transaction from the frozen-edge balance list. BalanceList balanceList = BalanceListManager.getFrozenEdgeList(); Transaction result = null; for (Transaction transaction : balanceList.getPendingCycleTransactions().values()) { if (ByteUtil.arraysAreEqual(signature, transaction.getSignature())) { result = transaction; } } return result; } private static Set<Node> fetchCycleNodes(List<ManagedVerifier> managedVerifiers) { Set<Node> cycleNodes = ConcurrentHashMap.newKeySet(); for (int i = 0; i < managedVerifiers.size() && cycleNodes.isEmpty(); i++) { Message meshRequest = new Message(MessageType.MeshRequest15, null); ManagedVerifier verifier = managedVerifiers.get(i); AtomicBoolean processedResponse = new AtomicBoolean(false); Message.fetchTcp(verifier.getHost(), verifier.getPort(), meshRequest, new MessageCallback() { @Override public void responseReceived(Message message) { if (message != null && (message.getContent() instanceof MeshResponse)) { MeshResponse response = (MeshResponse) message.getContent(); cycleNodes.addAll(response.getMesh()); } processedResponse.set(true); } }); // Wait up to 2 seconds for the response to be processed. for (int j = 0; j < 10 && !processedResponse.get(); j++) { ThreadUtil.sleep(200L); } } return cycleNodes; } private static List<ByteBuffer> fetchCycleOrder(List<ManagedVerifier> managedVerifiers) { List<ByteBuffer> cycleOrder = new CopyOnWriteArrayList<>(); for (int i = 0; i < managedVerifiers.size() && cycleOrder.isEmpty(); i++) { Message meshRequest = new Message(MessageType.BootstrapRequestV2_35, new BootstrapRequest()); ManagedVerifier verifier = managedVerifiers.get(i); AtomicBoolean processedResponse = new AtomicBoolean(false); Message.fetchTcp(verifier.getHost(), verifier.getPort(), meshRequest, new MessageCallback() { @Override public void responseReceived(Message message) { if (message != null && (message.getContent() instanceof BootstrapResponseV2)) { BootstrapResponseV2 response = (BootstrapResponseV2) message.getContent(); cycleOrder.addAll(response.getCycleVerifiers()); } processedResponse.set(true); } }); // Wait up to 2 seconds for the response to be processed. for (int j = 0; j < 10 && !processedResponse.get(); j++) { ThreadUtil.sleep(200L); } } return cycleOrder; } private static void sendSignatures(byte[] transactionSignature, byte vote, Set<Node> cycleNodes, List<ByteBuffer> cycleOrder, List<ManagedVerifier> managedVerifiers) { // For each managed verifier, create the signature transaction and message. CommandOutputConsole output = new CommandOutputConsole(); Set<PendingMessage> messages = ConcurrentHashMap.newKeySet(); for (ManagedVerifier verifier : managedVerifiers) { // Make the transaction. long timestamp = ClientTransactionUtil.suggestedTransactionTimestamp(); Transaction transaction = Transaction.cycleSignatureTransaction(timestamp, vote, transactionSignature, verifier.getSeed()); // Make a message to send the transaction to the next 5 verifiers in the cycle. int numberOfVerifiers = Math.min(5, cycleOrder.size()); for (int i = 0; i < numberOfVerifiers; i++) { byte[] identifier = cycleOrder.get(i).array(); for (Node node : cycleNodes) { if (ByteUtil.arraysAreEqual(identifier, node.getIdentifier())) { messages.add(new PendingMessage(node, MessageType.Transaction5, transaction, verifier.getSeed())); } } } } // Send the messages. ScriptUtil.sendMessages(messages, new CommandOutputConsole()); } }