23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass;
27 |
28 | import com.beust.jcommander.JCommander;
29 | import org.iota.jota.IotaAPI;
30 | import org.iota.jota.dto.response.GetNodeInfoResponse;
31 | import org.iota.jota.dto.response.GetTransactionsToApproveResponse;
32 | import org.iota.jota.error.ArgumentException;
33 | import org.iota.jota.model.Transaction;
34 | import org.apache.commons.lang3.NotImplementedException;
35 | import org.iota.compass.conf.ShadowingCoordinatorConfiguration;
36 | import org.iota.compass.crypto.Hasher;
37 | import org.slf4j.Logger;
38 | import org.slf4j.LoggerFactory;
39 |
40 | import java.io.IOException;
41 | import java.net.URL;
42 | import java.nio.file.Files;
43 | import java.nio.file.Paths;
44 | import java.util.ArrayList;
45 | import java.util.Comparator;
46 | import java.util.List;
47 | import java.util.stream.Collectors;
48 |
49 |
50 | /**
51 | * As opposed to the regular `org.iota.compass.Coordinator`, this coordinator will issue shadow milestones for an existing list of milestones.
52 | * This is useful if you want to migrate an existing Coordinator to a new seed or hashing method.
53 | *
54 | * !!! *NOTE* that the IRI node this ShadowingCoordinator talks to should already be configured to use the new Coordinator address !!!
55 | */
56 | public class ShadowingCoordinator {
57 | private final Logger log = LoggerFactory.getLogger(getClass());
58 |
59 | private final ShadowingCoordinatorConfiguration config;
60 | private final IotaAPI api;
61 | private final MilestoneSource db;
62 | private List oldMilestones;
63 |
64 | public ShadowingCoordinator(ShadowingCoordinatorConfiguration config, SignatureSource signatureSource) throws IOException {
65 | this.config = config;
66 |
67 | this.db = new MilestoneDatabase(config.powMode,
68 | config.powHost, signatureSource, config.layersPath);
69 | URL node = new URL(config.host);
70 | this.api = new IotaAPI.Builder()
71 | .protocol(node.getProtocol())
72 | .host(node.getHost())
73 | .port(node.getPort())
74 | .build();
75 | }
76 |
77 | public static void main(String[] args) throws Exception {
78 | ShadowingCoordinatorConfiguration config = new ShadowingCoordinatorConfiguration();
79 | JCommander.newBuilder()
80 | .addObject(config)
81 | .build()
82 | .parse(args);
83 |
84 | ShadowingCoordinator coo = new ShadowingCoordinator(config, SignatureSourceHelper.signatureSourceFromArgs(config.signatureSource, args));
85 | coo.setup();
86 | coo.start();
87 | }
88 |
89 | /**
90 | * Configures this `ShadowingCoordinator` instance and validates parameters
91 | *
92 | * @throws IOException if reading milestones CSV fails
93 | */
94 | private void setup() throws IOException {
95 | if (config.oldRoot != null) {
96 | throw new NotImplementedException("oldRoot");
97 | }
98 |
99 | if (config.milestonesCSV == null) {
100 | throw new IllegalArgumentException("Need a milestone csv");
101 | }
102 |
103 | this.oldMilestones = Files.readAllLines(Paths.get(config.milestonesCSV)).stream().map((String s) -> {
104 | String[] chunks = s.split(",");
105 | long idx = Long.parseLong(chunks[0]);
106 | String tail = chunks[1];
107 |
108 | return new OldMilestone(idx, tail);
109 | })
110 | .filter(m -> m.milestoneIdx >= config.oldMinIndex && m.milestoneIdx <= config.oldMaxIndex)
111 | .sorted(Comparator.comparingLong(o -> o.milestoneIdx))
112 | .collect(Collectors.toList());
113 |
114 | log.info("Loaded {} old milestones", oldMilestones.size());
115 | log.info("Old milestone indices (min, max): [{}, {}]", oldMilestones.get(0).milestoneIdx, oldMilestones.get(oldMilestones.size() - 1).milestoneIdx);
116 | }
117 |
118 | private void broadcast(List transactions) throws ArgumentException {
119 | log.info("Collected {} transactions for broadcast.", transactions.size());
120 |
121 | if (config.broadcast) {
122 | api.storeAndBroadcast(transactions.stream().map(Transaction::toTrytes).toArray(String[]::new));
123 | log.info("Broadcasted {} transactions.", transactions.size());
124 | } else {
125 | log.info("Skipping broadcast.");
126 | }
127 |
128 | transactions.clear();
129 | }
130 |
131 |
132 | private void start() throws Exception {
133 | String trunk = config.initialTrunk;
134 | String branch;
135 |
136 | int newMilestoneIdx = config.index;
137 | log.info("Starting milestone index: {}", newMilestoneIdx);
138 |
139 | List transactions = new ArrayList<>();
140 |
141 | for (OldMilestone oldMilestone : oldMilestones) {
142 | branch = oldMilestone.tail;
143 |
144 | List txs = db.createMilestone(trunk, branch, newMilestoneIdx, config.MWM);
145 | transactions.addAll(txs);
146 | log.info("Created milestone {}({}) referencing {} and {}", newMilestoneIdx, Hasher.hashTrytes(db.getPoWMode(),
147 | txs.get(0).toTrytes()), trunk, branch);
148 |
149 | /*
150 | * If the current list of transactions exceeds the broadcast threshold,
151 | * broadcast all available transactions.
152 | * Before continuing the milestone generation, ensures that node has become solid on the new milestones.
153 | */
154 | if (transactions.size() >= config.broadcastBatch) {
155 | broadcast(transactions);
156 |
157 | GetNodeInfoResponse nodeInfo;
158 | int count = 0;
159 | while (true) {
160 | nodeInfo = api.getNodeInfo();
161 | Thread.sleep(200);
162 | count++;
163 |
164 | if (nodeInfo.getLatestSolidSubtangleMilestoneIndex() != newMilestoneIdx) {
165 | continue;
166 | }
167 |
168 | try {
169 | GetTransactionsToApproveResponse txToApprove = api.getTransactionsToApprove(config.depth);
170 | log.info("{} Trunk: {} Branch: {}", count, txToApprove.getBranchTransaction(), txToApprove.getTrunkTransaction());
171 | if (txToApprove.getBranchTransaction() == null || txToApprove.getTrunkTransaction() == null) {
172 | throw new RuntimeException("Broke transactions to approve. Repeating check.");
173 | }
174 |
175 | break;
176 | } catch (Exception e) {
177 | log.error("Failed TX TO Approve at milestone: {}, {}", newMilestoneIdx, e.getMessage());
178 | }
179 | }
180 | }
181 |
182 |
183 | newMilestoneIdx++;
184 |
185 | trunk = Hasher.hashTrytes(db.getPoWMode(), txs.get(0).toTrytes());
186 | }
187 |
188 | broadcast(transactions);
189 |
190 | log.info("Shadowing complete.");
191 | }
192 |
193 | class OldMilestone {
194 | String tail;
195 | long milestoneIdx;
196 |
197 | public OldMilestone(long milestoneIdx, String tail) {
198 | this.tail = tail;
199 | this.milestoneIdx = milestoneIdx;
200 | }
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/compass/conf/BUILD:
--------------------------------------------------------------------------------
1 | java_library(
2 | name = "conf",
3 | srcs = glob(["*.java"]),
4 | visibility = ["//visibility:public"],
5 | deps = [
6 | "//compass/milestone",
7 | "//compass/sign:common",
8 | "@com_beust_jcommander//jar",
9 | "@com_google_guava_guava//jar",
10 | "@junit_junit//jar",
11 | "@org_iota_jota//jar",
12 | ],
13 | )
14 |
--------------------------------------------------------------------------------
/compass/conf/BaseConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass.conf;
27 |
28 | import com.beust.jcommander.Parameter;
29 | import org.iota.jota.pow.SpongeFactory;
30 | import org.iota.compass.SignatureSourceType;
31 |
32 | import java.net.URL;
33 |
34 | public class BaseConfiguration {
35 | @Parameter(names = "-layers", description = "Path to folder containing Merkle Tree layers", required = true)
36 | public String layersPath;
37 |
38 | @Parameter(names = "-host", description = "URL for IRI host", required = true)
39 | public String host;
40 |
41 | @Parameter(names = "-mwm", description = "Minimum Weight Magnitude", required = true)
42 | public int MWM = 9;
43 |
44 | @Parameter(names = "-broadcast", description = "Should Coordinator really broadcast milestones?")
45 | public boolean broadcast = false;
46 |
47 | @Parameter(names = "-powMode", description = "Sponge mode to use for Proof of Work (one of CURLP81, KERL)", required = true,
48 | converter = SpongeModeConverter.class, validateValueWith = {POWModeValidator.class})
49 | public SpongeFactory.Mode powMode = SpongeFactory.Mode.CURLP81;
50 |
51 | @Parameter(names = "-powHost", description = "Outsource CURLP81 PoW to an IRI host", required = false, converter = URLConverter.class)
52 | public URL powHost = null;
53 |
54 | @Parameter(names = "-signatureSource", description = "Signature source type (can be 'inmemory' or 'remote')", converter = SignatureSourceTypeConverter.class)
55 | public SignatureSourceType signatureSource = SignatureSourceType.INMEMORY;
56 | }
57 |
--------------------------------------------------------------------------------
/compass/conf/CoordinatorConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass.conf;
27 |
28 | import com.beust.jcommander.Parameter;
29 |
30 | import java.util.List;
31 | import java.util.ArrayList;
32 |
33 | public class CoordinatorConfiguration extends BaseConfiguration {
34 | @Parameter(names = "-bootstrap", description = "Bootstrap network")
35 | public boolean bootstrap = false;
36 |
37 | @Parameter(names = "-tick", description = "Milestone tick in milliseconds", required = true)
38 | public int tick = 15000;
39 |
40 | @Parameter(names = "-depth", description = "Starting depth")
41 | public int depth = 0;
42 |
43 | @Parameter(names = "-depthScale", description = "Time scale factor for depth decrease")
44 | public float depthScale = 1.01f;
45 |
46 | @Parameter(names = "-unsolidDelay", description = "Delay if node is not solid in milliseconds")
47 | public int unsolidDelay = 5000;
48 |
49 | @Parameter(names = "-inception", description = "Only use this if you know what you're doing.")
50 | public boolean inception = false;
51 |
52 | @Parameter(names = "-index", description = "Manually feed the current latest solid milestone index of IRI." +
53 | " So the next milestone will be index +1")
54 | public Integer index;
55 |
56 | @Parameter(names = "-validator", description = "Validator nodes to use")
57 | public List validators = new ArrayList<>();
58 |
59 | @Parameter(names = "-validationAttempts", description = "If tips validation fails, obtain new tips and validate up to this number of attempts.")
60 | public int validationAttempts = 10;
61 |
62 | @Parameter(names = "-validationDelay", description = "Obtained tips might not be solid right away on the validators, sleep the specified amount of seconds before validating.")
63 | public int validationDelay = 5;
64 |
65 | @Parameter(names = "-propagationRetriesThreshold", description = "Number of milestone propagation retries we attempt before failing.")
66 | public int propagationRetriesThreshold = 5;
67 |
68 | @Parameter(names = "-allowDifferentCooAddress", description = "Don't fail on different Coordinator Addresses")
69 | public boolean allowDifferentCooAddress = false;
70 |
71 | @Parameter(names = "-statePath", description = "Path to compass state file.")
72 | public String statePath = "compass.state";
73 |
74 | @Parameter(names = "-APIRetries", description = "Number of attempts to retry failing API call.")
75 | public int APIRetries = 5;
76 |
77 | @Parameter(names = "-APIRetryInterval", description = "Interval (in milliseconds) to wait between failing API attempts.")
78 | public int APIRetryInterval = 1000;
79 |
80 | @Parameter(names = "-referenceLastMilestone", description = "Generate a milestone that references the last and then exit")
81 | public boolean referenceLastMilestone = false;
82 | }
83 |
--------------------------------------------------------------------------------
/compass/conf/CoordinatorState.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass.conf;
27 |
28 | import java.io.Serializable;
29 | import java.util.Collections;
30 | import java.util.List;
31 |
32 | public class CoordinatorState implements Serializable {
33 |
34 | public int latestMilestoneIndex;
35 | public String latestMilestoneHash;
36 | public long latestMilestoneTime;
37 | public List latestMilestoneTransactions = Collections.EMPTY_LIST;
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/compass/conf/InMemorySignatureSourceConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass.conf;
27 |
28 | import com.beust.jcommander.Parameter;
29 | import org.iota.jota.pow.SpongeFactory;
30 |
31 | public class InMemorySignatureSourceConfiguration {
32 | @Parameter(names = "-seed", description = "Seed", required = true)
33 | public String seed;
34 |
35 | @Parameter(names = "-sigMode", description = "Sponge mode to use for signature creation (one of CURLP27, CURLP81, KERL)",
36 | required = true, converter = SpongeModeConverter.class)
37 | public SpongeFactory.Mode sigMode = SpongeFactory.Mode.CURLP27;
38 |
39 | @Parameter(names = "-security", description = "Security level to use. Value must be in [1;3]")
40 | public Integer security = 1;
41 | }
42 |
--------------------------------------------------------------------------------
/compass/conf/LayersCalculatorConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass.conf;
27 |
28 | import com.beust.jcommander.Parameter;
29 | import org.iota.compass.SignatureSourceType;
30 |
31 | public class LayersCalculatorConfiguration {
32 | @Parameter(names = "-layers", description = "Path to folder where Merkle Tree layers will be written to", required = true)
33 | public String layersPath;
34 |
35 | @Parameter(names = "-depth", description = "Depth the resulting merkle tree", required = true)
36 | public int depth;
37 |
38 | @Parameter(names = "-signatureSource", description = "Signature source type (can be 'inmemory' or 'remote')", converter = SignatureSourceTypeConverter.class)
39 | public SignatureSourceType signatureSource = SignatureSourceType.INMEMORY;
40 | }
41 |
--------------------------------------------------------------------------------
/compass/conf/POWModeValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass.conf;
27 |
28 | import com.beust.jcommander.IValueValidator;
29 | import com.beust.jcommander.ParameterException;
30 | import org.iota.jota.pow.SpongeFactory;
31 |
32 | public class POWModeValidator implements IValueValidator {
33 |
34 | @Override
35 | public void validate(String s, SpongeFactory.Mode mode) throws ParameterException {
36 | if (mode != SpongeFactory.Mode.CURLP81 && mode != SpongeFactory.Mode.KERL) {
37 | throw new ParameterException("Invalid mode provided for PoW. Must be CURLP81 or KERL.");
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/compass/conf/RemoteSignatureSourceConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass.conf;
27 |
28 | import com.beust.jcommander.Parameter;
29 |
30 | public class RemoteSignatureSourceConfiguration {
31 | @Parameter(names = "-remoteURI", description = "URI for remote signature source", required = true)
32 | public String uri;
33 |
34 | @Parameter(names = "-remotePlaintext", description = "Whether to communicate with signatureSource in plaintext")
35 | public boolean plaintext = false;
36 |
37 | @Parameter(names = "-remoteTrustCertCollection", description = "Path to trust cert collection for encrypted connection to remote signature source server")
38 | public String trustCertCollection = null;
39 |
40 | @Parameter(names = "-remoteClientCertChain", description = "Path to client certificate chain to use for authenticating to the remote signature source server")
41 | public String clientCertChain = null;
42 |
43 | @Parameter(names = "-remoteClientKey", description = "Path to private key to use for authenticating to the remote signature source server")
44 | public String clientKey = null;
45 | }
46 |
--------------------------------------------------------------------------------
/compass/conf/ShadowingCoordinatorConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass.conf;
27 |
28 | import com.beust.jcommander.Parameter;
29 | import org.iota.compass.MilestoneSource;
30 |
31 | public class ShadowingCoordinatorConfiguration extends BaseConfiguration {
32 | @Parameter(names = "-milestonesCSV", description = "csv (index, tail) of old milestones")
33 | public String milestonesCSV;
34 |
35 | @Parameter(names = "-oldRoot", description = "Old milestone address")
36 | public String oldRoot;
37 |
38 | @Parameter(names = "-oldMinIndex", description = "Minimum old milestone index (inclusive)")
39 | public long oldMinIndex = 0;
40 |
41 | @Parameter(names = "-oldMaxIndex", description = "Maximum old milestone index (inclusive)")
42 | public long oldMaxIndex = Long.MAX_VALUE;
43 |
44 | @Parameter(names = "-index", description = "Starting milestone index (inclusive)", required = true)
45 | public Integer index;
46 |
47 | @Parameter(names = "-broadcastBatch", description = "Rate at which broadcasts are batched")
48 | public int broadcastBatch = 666;
49 |
50 | @Parameter(names = "-initialTrunk", description = "Initial trunk that is referenced")
51 | public String initialTrunk = MilestoneSource.EMPTY_HASH;
52 |
53 | @Parameter(names = "-depth", description = "depth from which to start the random walk")
54 | public Integer depth = 3;
55 | }
56 |
--------------------------------------------------------------------------------
/compass/conf/SignatureSourceServerConfiguration.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass.conf;
2 |
3 | import com.beust.jcommander.Parameter;
4 |
5 | public class SignatureSourceServerConfiguration extends InMemorySignatureSourceConfiguration {
6 | @Parameter(names = "-port", description = "Port to listen on.")
7 | public Integer port = 50051;
8 |
9 | @Parameter(names = "-plaintext", description = "Whether to communicate with signatureSource in plaintext")
10 | public boolean plaintext = false;
11 |
12 | @Parameter(names = "-trustCertCollection", description = "Path to trust cert collection")
13 | public String trustCertCollection = null;
14 |
15 | @Parameter(names = "-certChain", description = "Path to certificate chain")
16 | public String certChain = null;
17 |
18 | @Parameter(names = "-privateKey ", description = "Path to the server's certificate's private key")
19 | public String privateKey = null;
20 | }
21 |
--------------------------------------------------------------------------------
/compass/conf/SignatureSourceTypeConverter.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass.conf;
2 |
3 | import com.beust.jcommander.IStringConverter;
4 | import org.iota.compass.SignatureSourceType;
5 |
6 | public class SignatureSourceTypeConverter implements IStringConverter {
7 | @Override
8 | public SignatureSourceType convert(String s) {
9 | return SignatureSourceType.valueOf(s.toUpperCase());
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/compass/conf/SpongeModeConverter.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass.conf;
2 |
3 | import com.beust.jcommander.IStringConverter;
4 | import org.iota.jota.pow.SpongeFactory;
5 |
6 | public class SpongeModeConverter implements IStringConverter {
7 | @Override
8 | public SpongeFactory.Mode convert(String s) {
9 | return SpongeFactory.Mode.valueOf(s);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/compass/conf/URLConverter.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass.conf;
2 |
3 | import com.beust.jcommander.IStringConverter;
4 | import com.beust.jcommander.ParameterException;
5 |
6 | import java.net.MalformedURLException;
7 | import java.net.URL;
8 |
9 | public class URLConverter implements IStringConverter {
10 | @Override
11 | public URL convert(String s) {
12 | try {
13 | return new URL(s);
14 | } catch (MalformedURLException e) {
15 | throw new ParameterException("Invalid URL provided as Remote PoW host.");
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/compass/crypto/BUILD:
--------------------------------------------------------------------------------
1 | MAIN_BASE_PATH = "src/main/java/org/iota/compass/%s"
2 |
3 | java_library(
4 | name = "crypto",
5 | srcs = [
6 | "Hasher.java",
7 | "ISS.java",
8 | "ISSInPlace.java",
9 | "KerlPoW.java",
10 | "RemoteCURLP81PoW.java",
11 | "IotaRemotePoW.java",
12 | ],
13 | visibility = ["//visibility:public"],
14 | deps = [
15 | "@com_google_guava_guava//jar",
16 | "@org_bouncycastle_bcprov_jdk15on//jar",
17 | "@org_iota_jota//jar",
18 | "@org_slf4j_slf4j_api//jar",
19 | ],
20 | )
21 |
--------------------------------------------------------------------------------
/compass/crypto/Hasher.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass.crypto;
27 |
28 | import org.iota.jota.pow.ICurl;
29 | import org.iota.jota.pow.JCurl;
30 | import org.iota.jota.pow.SpongeFactory;
31 | import org.iota.jota.utils.Converter;
32 |
33 | /**
34 | *
35 | */
36 | public class Hasher {
37 | /**
38 | * Hashes a provided Tryte string using the given method
39 | *
40 | * @param mode the sponge method to use
41 | * @param trytes
42 | * @return 81 tryte hash
43 | */
44 | public static String hashTrytes(SpongeFactory.Mode mode, String trytes) {
45 | return Converter.trytes(hashTrytesToTrits(mode, trytes));
46 | }
47 |
48 | public static int[] hashTrytesToTrits(SpongeFactory.Mode mode, String trytes) {
49 | int[] hash = new int[JCurl.HASH_LENGTH];
50 |
51 | ICurl sponge = SpongeFactory.create(mode);
52 | sponge.absorb(Converter.trits(trytes));
53 | sponge.squeeze(hash);
54 |
55 | return hash;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/compass/crypto/ISS.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass.crypto;
27 |
28 | import org.iota.jota.pow.ICurl;
29 | import org.iota.jota.pow.JCurl;
30 | import org.iota.jota.pow.SpongeFactory;
31 |
32 | import java.util.Arrays;
33 |
34 | public class ISS {
35 |
36 | public static final int NUMBER_OF_FRAGMENT_CHUNKS = 27;
37 | public static final int FRAGMENT_LENGTH = JCurl.HASH_LENGTH * NUMBER_OF_FRAGMENT_CHUNKS;
38 | public static final int TRYTE_WIDTH = 3;
39 | private static final int NUMBER_OF_SECURITY_LEVELS = 3;
40 | public static final int NORMALIZED_FRAGMENT_LENGTH = JCurl.HASH_LENGTH / TRYTE_WIDTH / NUMBER_OF_SECURITY_LEVELS;
41 | private static final int MIN_TRIT_VALUE = -1, MAX_TRIT_VALUE = 1;
42 | private static final int MIN_TRYTE_VALUE = -13, MAX_TRYTE_VALUE = 13;
43 |
44 | public static int[] subseed(SpongeFactory.Mode mode, final int[] seed, long index) {
45 |
46 | if (index < 0) {
47 | throw new RuntimeException("Invalid subseed index: " + index);
48 | }
49 |
50 | final int[] subseedPreimage = Arrays.copyOf(seed, seed.length);
51 |
52 | while (index-- > 0) {
53 |
54 | for (int i = 0; i < subseedPreimage.length; i++) {
55 |
56 | if (++subseedPreimage[i] > MAX_TRIT_VALUE) {
57 | subseedPreimage[i] = MIN_TRIT_VALUE;
58 | } else {
59 | break;
60 | }
61 | }
62 | }
63 |
64 | final int[] subseed = new int[JCurl.HASH_LENGTH];
65 |
66 | final ICurl hash = SpongeFactory.create(mode);
67 | hash.absorb(subseedPreimage, 0, subseedPreimage.length);
68 | hash.squeeze(subseed, 0, subseed.length);
69 | return subseed;
70 | }
71 |
72 | public static int[] key(SpongeFactory.Mode mode, final int[] subseed, final int numberOfFragments) {
73 |
74 | if (subseed.length != JCurl.HASH_LENGTH) {
75 | throw new RuntimeException("Invalid subseed length: " + subseed.length);
76 | }
77 | if (numberOfFragments <= 0) {
78 | throw new RuntimeException("Invalid number of key fragments: " + numberOfFragments);
79 | }
80 |
81 | final int[] key = new int[FRAGMENT_LENGTH * numberOfFragments];
82 |
83 | final ICurl hash = SpongeFactory.create(mode);
84 | hash.absorb(subseed, 0, subseed.length);
85 | hash.squeeze(key, 0, key.length);
86 | return key;
87 | }
88 |
89 | public static int[] digests(SpongeFactory.Mode mode, final int[] key) {
90 |
91 | if (key.length == 0 || key.length % FRAGMENT_LENGTH != 0) {
92 | throw new RuntimeException("Invalid key length: " + key.length);
93 | }
94 |
95 | final int[] digests = new int[key.length / FRAGMENT_LENGTH * JCurl.HASH_LENGTH];
96 | final ICurl hash = SpongeFactory.create(mode);
97 |
98 | for (int i = 0; i < key.length / FRAGMENT_LENGTH; i++) {
99 |
100 | final int[] buffer = Arrays.copyOfRange(key, i * FRAGMENT_LENGTH, (i + 1) * FRAGMENT_LENGTH);
101 | for (int j = 0; j < NUMBER_OF_FRAGMENT_CHUNKS; j++) {
102 |
103 | for (int k = MAX_TRYTE_VALUE - MIN_TRYTE_VALUE; k-- > 0; ) {
104 | hash.reset();
105 | hash.absorb(buffer, j * JCurl.HASH_LENGTH, JCurl.HASH_LENGTH);
106 | hash.squeeze(buffer, j * JCurl.HASH_LENGTH, JCurl.HASH_LENGTH);
107 | }
108 | }
109 | hash.reset();
110 | hash.absorb(buffer, 0, buffer.length);
111 | hash.squeeze(digests, i * JCurl.HASH_LENGTH, JCurl.HASH_LENGTH);
112 | }
113 |
114 | return digests;
115 | }
116 |
117 | public static int[] address(SpongeFactory.Mode mode, final int[] digests) {
118 |
119 | if (digests.length == 0 || digests.length % JCurl.HASH_LENGTH != 0) {
120 | throw new RuntimeException("Invalid digests length: " + digests.length);
121 | }
122 |
123 | final int[] address = new int[JCurl.HASH_LENGTH];
124 |
125 | final ICurl hash = SpongeFactory.create(mode);
126 | hash.absorb(digests, 0, digests.length);
127 | hash.squeeze(address, 0, address.length);
128 |
129 | return address;
130 | }
131 |
132 | public static int[] normalizedBundle(final int[] bundle) {
133 |
134 | if (bundle.length != JCurl.HASH_LENGTH) {
135 | throw new RuntimeException("Invalid bundleValidator length: " + bundle.length);
136 | }
137 |
138 | final int[] normalizedBundle = new int[JCurl.HASH_LENGTH / TRYTE_WIDTH];
139 |
140 | for (int i = 0; i < NUMBER_OF_SECURITY_LEVELS; i++) {
141 |
142 | int sum = 0;
143 | for (int j = i * (JCurl.HASH_LENGTH / TRYTE_WIDTH / NUMBER_OF_SECURITY_LEVELS); j < (i + 1) * (JCurl.HASH_LENGTH / TRYTE_WIDTH / NUMBER_OF_SECURITY_LEVELS); j++) {
144 |
145 | normalizedBundle[j] = bundle[j * TRYTE_WIDTH] + bundle[j * TRYTE_WIDTH + 1] * 3 + bundle[j * TRYTE_WIDTH + 2] * 9;
146 | sum += normalizedBundle[j];
147 | }
148 | if (sum > 0) {
149 |
150 | while (sum-- > 0) {
151 |
152 | for (int j = i * (JCurl.HASH_LENGTH / TRYTE_WIDTH / NUMBER_OF_SECURITY_LEVELS); j < (i + 1) * (JCurl.HASH_LENGTH / TRYTE_WIDTH / NUMBER_OF_SECURITY_LEVELS); j++) {
153 |
154 | if (normalizedBundle[j] > MIN_TRYTE_VALUE) {
155 | normalizedBundle[j]--;
156 | break;
157 | }
158 | }
159 | }
160 |
161 | } else {
162 |
163 | while (sum++ < 0) {
164 |
165 | for (int j = i * (JCurl.HASH_LENGTH / TRYTE_WIDTH / NUMBER_OF_SECURITY_LEVELS); j < (i + 1) * (JCurl.HASH_LENGTH / TRYTE_WIDTH / NUMBER_OF_SECURITY_LEVELS); j++) {
166 |
167 | if (normalizedBundle[j] < MAX_TRYTE_VALUE) {
168 | normalizedBundle[j]++;
169 | break;
170 | }
171 | }
172 | }
173 | }
174 | }
175 |
176 | return normalizedBundle;
177 | }
178 |
179 | public static int[] signatureFragment(SpongeFactory.Mode mode, final int[] normalizedBundleFragment, final int[] keyFragment) {
180 |
181 | if (normalizedBundleFragment.length != NORMALIZED_FRAGMENT_LENGTH) {
182 | throw new RuntimeException("Invalid normalized bundleValidator fragment length: " + normalizedBundleFragment.length);
183 | }
184 | if (keyFragment.length != FRAGMENT_LENGTH) {
185 | throw new RuntimeException("Invalid key fragment length: " + keyFragment.length);
186 | }
187 |
188 | final int[] signatureFragment = Arrays.copyOf(keyFragment, keyFragment.length);
189 | final ICurl hash = SpongeFactory.create(mode);
190 |
191 | for (int j = 0; j < NUMBER_OF_FRAGMENT_CHUNKS; j++) {
192 |
193 | for (int k = MAX_TRYTE_VALUE - normalizedBundleFragment[j]; k-- > 0; ) {
194 | hash.reset();
195 | hash.absorb(signatureFragment, j * JCurl.HASH_LENGTH, JCurl.HASH_LENGTH);
196 | hash.squeeze(signatureFragment, j * JCurl.HASH_LENGTH, JCurl.HASH_LENGTH);
197 | }
198 | }
199 |
200 | return signatureFragment;
201 | }
202 |
203 | public static int[] digest(SpongeFactory.Mode mode, final int[] normalizedBundleFragment, final int[] signatureFragment) {
204 |
205 | if (normalizedBundleFragment.length != JCurl.HASH_LENGTH / TRYTE_WIDTH / NUMBER_OF_SECURITY_LEVELS) {
206 | throw new RuntimeException("Invalid normalized bundleValidator fragment length: " + normalizedBundleFragment.length);
207 | }
208 | if (signatureFragment.length != FRAGMENT_LENGTH) {
209 | throw new RuntimeException("Invalid signature fragment length: " + signatureFragment.length);
210 | }
211 |
212 | final int[] digest = new int[JCurl.HASH_LENGTH];
213 | final int[] buffer = Arrays.copyOf(signatureFragment, FRAGMENT_LENGTH);
214 | final ICurl hash = SpongeFactory.create(mode);
215 | for (int j = 0; j < NUMBER_OF_FRAGMENT_CHUNKS; j++) {
216 |
217 | for (int k = normalizedBundleFragment[j] - MIN_TRYTE_VALUE; k-- > 0; ) {
218 | hash.reset();
219 | hash.absorb(buffer, j * JCurl.HASH_LENGTH, JCurl.HASH_LENGTH);
220 | hash.squeeze(buffer, j * JCurl.HASH_LENGTH, JCurl.HASH_LENGTH);
221 | }
222 | }
223 | hash.reset();
224 | hash.absorb(buffer, 0, buffer.length);
225 | hash.squeeze(digest, 0, digest.length);
226 |
227 | return digest;
228 | }
229 |
230 | public static int[] getMerkleRoot(SpongeFactory.Mode mode, final int[] inHash, int[] trits, int offset, final int indexIn, int size) {
231 | int index = indexIn;
232 | int[] hash = inHash.clone();
233 | final ICurl curl = SpongeFactory.create(mode);
234 | for (int i = 0; i < size; i++) {
235 | curl.reset();
236 | if ((index & 1) == 0) {
237 | curl.absorb(hash, 0, hash.length);
238 | curl.absorb(trits, offset + i * JCurl.HASH_LENGTH, JCurl.HASH_LENGTH);
239 | } else {
240 | curl.absorb(trits, offset + i * JCurl.HASH_LENGTH, JCurl.HASH_LENGTH);
241 | curl.absorb(hash, 0, hash.length);
242 | }
243 | curl.squeeze(hash, 0, hash.length);
244 |
245 | index >>= 1;
246 | }
247 | if (index != 0) {
248 | return new int[JCurl.HASH_LENGTH];
249 | }
250 | return hash;
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/compass/crypto/ISSInPlace.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass.crypto;
27 |
28 | import org.iota.jota.pow.ICurl;
29 | import org.iota.jota.pow.JCurl;
30 | import org.iota.jota.pow.SpongeFactory;
31 |
32 | import java.util.Arrays;
33 |
34 | /**
35 | * (c) 2016 Come-from-Beyond
36 | */
37 | public class ISSInPlace {
38 |
39 | public static final int NUMBER_OF_FRAGMENT_CHUNKS = 27;
40 | public static final int FRAGMENT_LENGTH = JCurl.HASH_LENGTH * NUMBER_OF_FRAGMENT_CHUNKS;
41 | public static final int TRYTE_WIDTH = 3;
42 | private static final int NUMBER_OF_SECURITY_LEVELS = 3;
43 | public static final int NORMALIZED_FRAGMENT_LENGTH = JCurl.HASH_LENGTH / TRYTE_WIDTH / NUMBER_OF_SECURITY_LEVELS;
44 | private static final int MIN_TRIT_VALUE = -1, MAX_TRIT_VALUE = 1;
45 | private static final int MIN_TRYTE_VALUE = -13, MAX_TRYTE_VALUE = 13;
46 |
47 | public static void subseed(SpongeFactory.Mode mode, int[] subseed, long index) {
48 |
49 | if (index < 0) {
50 | throw new RuntimeException("Invalid subseed index: " + index);
51 | }
52 |
53 | if (subseed.length != JCurl.HASH_LENGTH) {
54 | throw new IllegalArgumentException("Subseed array is not of HASH_LENGTH");
55 | }
56 |
57 | while (index-- > 0) {
58 | for (int i = 0; i < subseed.length; i++) {
59 |
60 | if (++subseed[i] > MAX_TRIT_VALUE) {
61 | subseed[i] = MIN_TRIT_VALUE;
62 | } else {
63 | break;
64 | }
65 | }
66 | }
67 |
68 | final ICurl hash = SpongeFactory.create(mode);
69 | hash.absorb(subseed, 0, subseed.length);
70 | hash.squeeze(subseed, 0, subseed.length);
71 | }
72 |
73 | public static void key(SpongeFactory.Mode mode, final int[] subseed, int[] key) {
74 |
75 | if (subseed.length != JCurl.HASH_LENGTH) {
76 | throw new RuntimeException("Invalid subseed length: " + subseed.length);
77 | }
78 |
79 | if ((key.length % FRAGMENT_LENGTH) != 0) {
80 | throw new IllegalArgumentException("key length must be multiple of fragment length");
81 | }
82 |
83 | int numberOfFragments = key.length / FRAGMENT_LENGTH;
84 |
85 | if (numberOfFragments <= 0) {
86 | throw new RuntimeException("Invalid number of key fragments: " + numberOfFragments);
87 | }
88 |
89 | final ICurl hash = SpongeFactory.create(mode);
90 | hash.absorb(subseed, 0, subseed.length);
91 | hash.squeeze(key, 0, key.length);
92 | }
93 |
94 | public static void digests(SpongeFactory.Mode mode, final int[] key, int[] digests) {
95 |
96 | if (key.length == 0 || key.length % FRAGMENT_LENGTH != 0) {
97 | throw new RuntimeException("Invalid key length: " + key.length);
98 | }
99 |
100 | if (digests.length != (key.length / FRAGMENT_LENGTH * JCurl.HASH_LENGTH)) {
101 | throw new IllegalArgumentException("Invalid digests length");
102 | }
103 |
104 | final ICurl hash = SpongeFactory.create(mode);
105 |
106 | for (int i = 0; i < key.length / FRAGMENT_LENGTH; i++) {
107 |
108 | final int[] buffer = Arrays.copyOfRange(key, i * FRAGMENT_LENGTH, (i + 1) * FRAGMENT_LENGTH);
109 | for (int j = 0; j < NUMBER_OF_FRAGMENT_CHUNKS; j++) {
110 |
111 | for (int k = MAX_TRYTE_VALUE - MIN_TRYTE_VALUE; k-- > 0; ) {
112 | hash.reset();
113 | hash.absorb(buffer, j * JCurl.HASH_LENGTH, JCurl.HASH_LENGTH);
114 | hash.squeeze(buffer, j * JCurl.HASH_LENGTH, JCurl.HASH_LENGTH);
115 | }
116 | }
117 | hash.reset();
118 | hash.absorb(buffer, 0, buffer.length);
119 | hash.squeeze(digests, i * JCurl.HASH_LENGTH, JCurl.HASH_LENGTH);
120 | }
121 | }
122 |
123 | public static void address(SpongeFactory.Mode mode, final int[] digests, int[] address) {
124 |
125 | if (digests.length == 0 || digests.length % JCurl.HASH_LENGTH != 0) {
126 | throw new RuntimeException("Invalid digests length: " + digests.length);
127 | }
128 |
129 | if (address.length != JCurl.HASH_LENGTH) {
130 | throw new IllegalArgumentException("Invalid address length");
131 | }
132 |
133 | final ICurl hash = SpongeFactory.create(mode);
134 | hash.absorb(digests, 0, digests.length);
135 | hash.squeeze(address, 0, address.length);
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/compass/crypto/IotaRemotePoW.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass.crypto;
2 |
3 | import org.iota.jota.IotaPoW;
4 |
5 | public interface IotaRemotePoW extends IotaPoW { }
6 |
--------------------------------------------------------------------------------
/compass/crypto/KerlPoW.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass.crypto;
27 |
28 | import org.iota.jota.pow.IotaLocalPoW;
29 | import org.iota.jota.pow.ICurl;
30 | import org.iota.jota.pow.SpongeFactory;
31 | import org.iota.jota.utils.Converter;
32 | import org.slf4j.Logger;
33 | import org.slf4j.LoggerFactory;
34 |
35 | import java.util.List;
36 | import java.util.concurrent.*;
37 | import java.util.concurrent.atomic.AtomicBoolean;
38 | import java.util.stream.Collectors;
39 | import java.util.stream.IntStream;
40 |
41 | /**
42 | * A simple man's naive and single-threaded implementation of Kerl-based Proof-of-Work
43 | */
44 | public class KerlPoW implements IotaLocalPoW {
45 | private static final Logger log = LoggerFactory.getLogger("KerlPoW");
46 |
47 | private final static int NONCE_START_TRIT = 7938;
48 | private final static int NONCE_LENGTH_TRIT = 81;
49 | private final static int NONCE_START_TRYTE = NONCE_START_TRIT / 3;
50 | public final static int NONCE_LENGTH_TRYTE = NONCE_LENGTH_TRIT / 3;
51 |
52 | private KerlPoWSettings settings;
53 |
54 | public KerlPoW() {
55 | this(new KerlPoWSettings());
56 | }
57 |
58 | private KerlPoW(KerlPoWSettings settings) {
59 | this.settings = settings;
60 | if (settings.numberOfThreads <= 0) {
61 | int available = Runtime.getRuntime().availableProcessors();
62 | settings.numberOfThreads = Math.max(1, Math.floorDiv(available * 8, 10));
63 | }
64 |
65 | // TODO (th0br0): fix PoW offsetting so we can use multiple threads
66 | settings.numberOfThreads = 1;
67 | }
68 |
69 | @Override
70 | public String performPoW(String trytes, int minWeightMagnitude) {
71 | final ExecutorService executorService = Executors.newFixedThreadPool(settings.numberOfThreads);
72 | final AtomicBoolean resultFound = new AtomicBoolean(false);
73 | final List searchers = IntStream.range(0, settings.numberOfThreads)
74 | .mapToObj((idx) -> new Searcher(trytes, resultFound, minWeightMagnitude))
75 | .collect(Collectors.toList());
76 | final List> searcherFutures = searchers.stream()
77 | .map(executorService::submit)
78 | .collect(Collectors.toList());
79 |
80 | executorService.shutdown();
81 | try {
82 | executorService.awaitTermination(10, TimeUnit.MINUTES);
83 |
84 | for (Future f : searcherFutures) {
85 | if (f.isDone() && f.get() != null) {
86 | return trytes.substring(0, NONCE_START_TRYTE) + f.get();
87 | }
88 | }
89 | } catch (ExecutionException | InterruptedException e) {
90 | log.error("failed to calculate PoW with MWM: {} , trytes: {}", trytes, minWeightMagnitude, e);
91 | return null;
92 | }
93 |
94 | return null;
95 | }
96 |
97 | private static class KerlPoWSettings {
98 | private int numberOfThreads = 1;
99 | }
100 |
101 | class Searcher implements Callable {
102 |
103 | private final AtomicBoolean resultFound;
104 | private final int targetZeros;
105 |
106 | private int[] trits;
107 | private int[] hashTrits = new int[243];
108 |
109 | public Searcher(String inputTrytes, AtomicBoolean resultFound, int targetZeros) {
110 | this.resultFound = resultFound;
111 | this.trits = Converter.trits(inputTrytes);
112 | this.targetZeros = targetZeros;
113 | }
114 |
115 | private boolean shouldAbort() {
116 | return resultFound.get();
117 | }
118 |
119 | private void increment(int[] trits, int offset, int size) {
120 | for (int i = offset; i < (offset + size) && ++trits[i] > 1; ++i) {
121 | trits[i] = -1;
122 | }
123 | }
124 |
125 | private int trailingZeros(int[] trits) {
126 | int count = 0;
127 | for (int i = trits.length - 1; i >= 0 && trits[i] == 0; i--) {
128 | count++;
129 | }
130 |
131 | return count;
132 | }
133 |
134 | private void search() {
135 | ICurl sponge = SpongeFactory.create(SpongeFactory.Mode.KERL);
136 | increment(trits, NONCE_START_TRIT, NONCE_LENGTH_TRIT);
137 |
138 | sponge.absorb(trits);
139 | sponge.squeeze(hashTrits);
140 | }
141 |
142 | @Override
143 | public String call() {
144 | String result = null;
145 | while (!shouldAbort()) {
146 | search();
147 |
148 | if (trailingZeros(hashTrits) >= targetZeros) {
149 | result = Converter.trytes(trits, NONCE_START_TRIT, NONCE_LENGTH_TRIT);
150 | resultFound.set(true);
151 | break;
152 | }
153 | }
154 |
155 | return result;
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/compass/crypto/RemoteCURLP81PoW.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass.crypto;
2 |
3 | import org.iota.jota.IotaAPI;
4 | import org.iota.jota.dto.response.GetAttachToTangleResponse;
5 | import org.iota.jota.error.ArgumentException;
6 | import org.iota.jota.model.Transaction;
7 |
8 | import java.net.URL;
9 |
10 | public class RemoteCURLP81PoW implements IotaRemotePoW {
11 |
12 | private final URL powHost;
13 |
14 | public RemoteCURLP81PoW(URL powHost) {
15 | this.powHost = powHost;
16 | }
17 |
18 | @Override
19 | public String performPoW(String trytes, int minWeightMagnitude) throws ArgumentException {
20 | // Build API object each time, preventing network changes between PoWs
21 | IotaAPI api = new IotaAPI.Builder()
22 | .protocol(powHost.getProtocol())
23 | .host(powHost.getHost())
24 | .port(powHost.getPort())
25 | .build();
26 | Transaction txSiblings = Transaction.asTransactionObject(trytes);
27 | GetAttachToTangleResponse res = api.attachToTangle(
28 | txSiblings.getTrunkTransaction(),
29 | txSiblings.getBranchTransaction(),
30 | minWeightMagnitude,
31 | trytes);
32 | // We sent only one big chunk of trytes
33 | return res.getTrytes()[0];
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/compass/exceptions/BUILD:
--------------------------------------------------------------------------------
1 | MAIN_BASE_PATH = "src/main/java/org/iota/compass/%s"
2 |
3 | java_library(
4 | name = "exceptions",
5 | srcs = glob([
6 | "*.java",
7 | ]),
8 | visibility = ["//visibility:public"],
9 | )
10 |
--------------------------------------------------------------------------------
/compass/exceptions/TimeoutException.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass.exceptions;
2 |
3 | /**
4 | * Thrown if an API call to IRI is timed out
5 | */
6 | public class TimeoutException extends Exception {
7 |
8 | public TimeoutException() {
9 | }
10 |
11 | public TimeoutException(String message) {
12 | super(message);
13 | }
14 |
15 | public TimeoutException(String message, Throwable cause) {
16 | super(message, cause);
17 | }
18 |
19 | public TimeoutException(Throwable cause) {
20 | super(cause);
21 | }
22 |
23 | public TimeoutException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
24 | super(message, cause, enableSuppression, writableStackTrace);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/compass/milestone/BUILD:
--------------------------------------------------------------------------------
1 | MAIN_BASE_PATH = "src/main/java/org/iota/compass/%s"
2 |
3 | java_library(
4 | name = "milestone",
5 | srcs = [
6 | "MilestoneDatabase.java",
7 | "MilestoneSource.java",
8 | ],
9 | visibility = ["//visibility:public"],
10 | deps = [
11 | "//compass/crypto",
12 | "//compass/sign:common",
13 | "@com_google_guava_guava//jar",
14 | "@org_bouncycastle_bcprov_jdk15on//jar",
15 | "@org_iota_jota//jar",
16 | "@org_slf4j_slf4j_api//jar",
17 | ],
18 | )
19 |
20 | java_test(
21 | name = "test_milestone",
22 | srcs = ["MilestoneTest.java"],
23 | flaky = True,
24 | test_class = "org.iota.compass.MilestoneTest",
25 | deps = [
26 | ":milestone",
27 | "//compass:layers_calculator",
28 | "//compass/conf",
29 | "//compass/crypto",
30 | "//compass/sign:common",
31 | "//compass/sign:inmemory",
32 | "//compass/sign:remote",
33 | "//compass/sign:server",
34 | "//compass/test",
35 | "@com_google_guava_guava//jar",
36 | "@junit_junit//jar",
37 | "@org_iota_jota//jar",
38 | ],
39 | )
40 |
--------------------------------------------------------------------------------
/compass/milestone/MilestoneDatabase.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass;
27 |
28 | import com.google.common.base.Strings;
29 | import org.iota.jota.IotaPoW;
30 | import org.iota.jota.model.Bundle;
31 | import org.iota.jota.model.Transaction;
32 | import org.iota.jota.pow.ICurl;
33 | import org.iota.jota.pow.SpongeFactory;
34 | import org.iota.jota.pow.pearldiver.PearlDiverLocalPoW;
35 | import org.iota.jota.utils.Converter;
36 | import org.iota.compass.crypto.*;
37 | import org.slf4j.Logger;
38 | import org.slf4j.LoggerFactory;
39 |
40 | import java.io.BufferedReader;
41 | import java.io.FileReader;
42 | import java.io.IOException;
43 | import java.net.URL;
44 | import java.nio.file.Files;
45 | import java.nio.file.Path;
46 | import java.nio.file.Paths;
47 | import java.util.*;
48 | import java.util.stream.Collectors;
49 | import java.util.stream.IntStream;
50 |
51 | import static org.iota.jota.pow.JCurl.HASH_LENGTH;
52 |
53 | public class MilestoneDatabase extends MilestoneSource {
54 |
55 | private static final Logger log = LoggerFactory.getLogger(MilestoneDatabase.class);
56 | private static final int NONCE_OFFSET = 2673 /* tx length in trytes */ - 27 /* nonce length in trytes */;
57 | private static final int SIGNATURE_LENGTH = 27 * 81;
58 | private static final int OFFSET = (ISS.FRAGMENT_LENGTH / 3);
59 | private static final int LENGTH = (243 + 81 + 81 + 27 + 27 + 27) / 3;
60 |
61 | private final SpongeFactory.Mode powMode;
62 | private final URL powHost;
63 | private final SignatureSource signatureSource;
64 | private final String root;
65 | private final List> layers;
66 |
67 |
68 | public MilestoneDatabase(SpongeFactory.Mode powMode, URL powHost, SignatureSource signatureSource, String path) throws IOException {
69 | this(powMode, powHost, signatureSource, loadLayers(path));
70 | }
71 |
72 | public MilestoneDatabase(SpongeFactory.Mode powMode, URL powHost, SignatureSource signatureSource, List> layers) {
73 | root = layers.get(0).get(0);
74 | this.layers = layers;
75 | this.signatureSource = signatureSource;
76 | this.powMode = powMode;
77 | this.powHost = powHost;
78 | }
79 |
80 | private static List readLines(Path p, int totalSize) throws IOException {
81 | BufferedReader br = new BufferedReader(new FileReader(p.toString()));
82 | List result = new ArrayList<>(totalSize);
83 | String line;
84 | do {
85 | line = br.readLine();
86 | if (line != null) {
87 | result.add(line);
88 | }
89 | } while (line != null);
90 |
91 | return result;
92 | }
93 |
94 | private static List> loadLayers(String path) throws IOException {
95 | Map> result = new HashMap<>();
96 |
97 | for (Path p : Files.newDirectoryStream(Paths.get(path))) {
98 | int idx = Integer.parseInt(p.toString().split("\\.")[1]);
99 | int totalSize = 1 << idx;
100 | try {
101 | result.put(idx, readLines(p, totalSize));
102 | } catch (IOException e) {
103 | log.error("failed to load layers from: {}", path, e);
104 | }
105 | }
106 |
107 | return IntStream.range(0, result.size())
108 | .mapToObj(result::get)
109 | .peek(list -> Objects.requireNonNull(list, "Found a missing layer. please check: " + path))
110 | .collect(Collectors.toList());
111 | }
112 |
113 | /**
114 | * Calculates a list of siblings
115 | *
116 | * @param leafIdx index of leaf
117 | * @param layers the Merkle tree in layers structure
118 | * @return a list of siblings
119 | */
120 | private static List siblings(int leafIdx, List> layers) {
121 | List siblings = new ArrayList<>(layers.size());
122 |
123 | int curLayer = layers.size() - 1;
124 |
125 | while (curLayer > 0) {
126 | List layer = layers.get(curLayer);
127 | if ((leafIdx & 1) == 1) {
128 | // odd
129 | siblings.add(layer.get(leafIdx - 1));
130 | } else {
131 | siblings.add(layer.get(leafIdx + 1));
132 | }
133 |
134 | leafIdx /= 2;
135 | curLayer--;
136 | }
137 |
138 | return siblings;
139 | }
140 |
141 | @Override
142 | public SpongeFactory.Mode getPoWMode() {
143 | return powMode;
144 | }
145 |
146 | @Override
147 | public String getRoot() {
148 | return root;
149 | }
150 |
151 | private IotaPoW getPoWProvider() {
152 | if (powMode == SpongeFactory.Mode.KERL) {
153 | return new KerlPoW();
154 | } else {
155 | if (powHost != null) {
156 | return new RemoteCURLP81PoW(powHost);
157 | } else {
158 | return new PearlDiverLocalPoW();
159 | }
160 | }
161 | }
162 |
163 | private String getTagForIndex(int index) {
164 | String tag;
165 | int[] trits = new int[15];
166 | for (int i = 0; i < index; i++) {
167 | Converter.increment(trits, trits.length);
168 | }
169 | tag = Converter.trytes(trits);
170 | return Strings.padEnd(tag, 27, '9');
171 | }
172 |
173 | @Override
174 | public List createMilestone(String trunk, String branch, int index, int mwm) {
175 |
176 | IotaPoW pow = getPoWProvider();
177 |
178 | // Get the siblings in the current merkle tree
179 | List leafSiblings = siblings(index, layers);
180 | String siblingsTrytes = String.join("", leafSiblings);
181 | String paddedSiblingsTrytes = Strings.padEnd(siblingsTrytes, ISS.FRAGMENT_LENGTH / ISS.TRYTE_WIDTH, '9');
182 |
183 | final String tag = getTagForIndex(index);
184 |
185 | // A milestone consists of two transactions.
186 | // The last transaction (currentIndex == lastIndex) contains the siblings for the merkle tree.
187 | Transaction txSiblings = new Transaction();
188 | txSiblings.setSignatureFragments(paddedSiblingsTrytes);
189 | txSiblings.setAddress(root);
190 | txSiblings.setCurrentIndex(signatureSource.getSecurity());
191 | txSiblings.setLastIndex(signatureSource.getSecurity());
192 | txSiblings.setTimestamp(System.currentTimeMillis() / 1000);
193 | txSiblings.setObsoleteTag(tag);
194 | txSiblings.setValue(0);
195 | txSiblings.setBundle(EMPTY_HASH);
196 | txSiblings.setTrunkTransaction(trunk);
197 | txSiblings.setBranchTransaction(branch);
198 | txSiblings.setTag(tag);
199 | txSiblings.setNonce(EMPTY_TAG);
200 |
201 | // The other transactions contain a signature that signs the siblings and thereby ensures the integrity.
202 | List txs =
203 | IntStream.range(0, signatureSource.getSecurity()).mapToObj(i -> {
204 | Transaction tx = new Transaction();
205 | tx.setSignatureFragments(Strings.repeat("9", 27 * 81));
206 | tx.setAddress(root);
207 | tx.setCurrentIndex(i);
208 | tx.setLastIndex(signatureSource.getSecurity());
209 | tx.setTimestamp(System.currentTimeMillis() / 1000);
210 | tx.setObsoleteTag(tag);
211 | tx.setValue(0);
212 | tx.setBundle(EMPTY_HASH);
213 | tx.setTrunkTransaction(EMPTY_HASH);
214 | tx.setBranchTransaction(trunk);
215 | tx.setTag(tag);
216 | tx.setNonce(EMPTY_TAG);
217 | return tx;
218 | }).collect(Collectors.toList());
219 |
220 | txs.add(txSiblings);
221 |
222 | Transaction tPoW;
223 | String hashToSign;
224 |
225 | //calculate the bundle hash (same for Curl & Kerl)
226 | String bundleHash = calculateBundleHash(txs);
227 | txs.forEach(tx -> tx.setBundle(bundleHash));
228 |
229 | txSiblings.setAttachmentTimestamp(System.currentTimeMillis());
230 | tPoW = new Transaction(pow.performPoW(txSiblings.toTrytes(), mwm));
231 | txSiblings.setAttachmentTimestamp(tPoW.getAttachmentTimestamp());
232 | txSiblings.setAttachmentTimestampLowerBound(tPoW.getAttachmentTimestampLowerBound());
233 | txSiblings.setAttachmentTimestampUpperBound(tPoW.getAttachmentTimestampUpperBound());
234 | txSiblings.setNonce(tPoW.getNonce());
235 |
236 | // We need to avoid the M bug we we are signing with KERL
237 | if (signatureSource.getSignatureMode() == SpongeFactory.Mode.KERL) {
238 | /*
239 | In the case that the signature is created using KERL, we need to ensure that there exists no 'M'(=13) in the
240 | normalized fragment that we're signing.
241 | */
242 | boolean hashContainsM;
243 | int attempts = 0;
244 | do {
245 | int[] hashTrits = Hasher.hashTrytesToTrits(powMode, txSiblings.toTrytes());
246 | int[] normHash = ISS.normalizedBundle(hashTrits);
247 |
248 | hashContainsM = Arrays.stream(normHash).limit(ISS.NUMBER_OF_FRAGMENT_CHUNKS * signatureSource.getSecurity()).anyMatch(elem -> elem == 13);
249 | if (hashContainsM) {
250 | txSiblings.setAttachmentTimestamp(System.currentTimeMillis());
251 | tPoW = new Transaction(pow.performPoW(txSiblings.toTrytes(), mwm));
252 | txSiblings.setAttachmentTimestamp(tPoW.getAttachmentTimestamp());
253 | txSiblings.setAttachmentTimestampLowerBound(tPoW.getAttachmentTimestampLowerBound());
254 | txSiblings.setAttachmentTimestampUpperBound(tPoW.getAttachmentTimestampUpperBound());
255 | txSiblings.setNonce(tPoW.getNonce());
256 | }
257 | attempts++;
258 | } while (hashContainsM);
259 |
260 | log.info("KERL milestone generation took {} attempts.", attempts);
261 |
262 | }
263 |
264 | hashToSign = Hasher.hashTrytes(powMode, txSiblings.toTrytes());
265 | String signature = signatureSource.getSignature(index, hashToSign);
266 | txSiblings.setHash(hashToSign);
267 |
268 | validateSignature(root, index, hashToSign, signature, siblingsTrytes);
269 |
270 | chainTransactionsFillSignatures(mwm, txs, signature);
271 |
272 | return txs;
273 | }
274 |
275 | private void validateSignature(String root, int index, String hashToSign, String signature, String siblingsTrytes) {
276 | int[] rootTrits = Converter.trits(root);
277 | int[] signatureTrits = Converter.trits(signature);
278 | int[] siblingsTrits = Converter.trits(siblingsTrytes);
279 | SpongeFactory.Mode mode = signatureSource.getSignatureMode();
280 |
281 | int[][] normalizedBundleFragments = new int[3][27];
282 |
283 |
284 | {
285 | int[] normalizedBundleHash = new Bundle().normalizedBundle(hashToSign);
286 |
287 | // Split hash into 3 fragments
288 | for (int i = 0; i < 3; i++) {
289 | normalizedBundleFragments[i] = Arrays.copyOfRange(normalizedBundleHash, i * 27, (i + 1) * 27);
290 | }
291 | }
292 |
293 | // Get digests
294 | int[] digests = new int[signatureSource.getSecurity() * HASH_LENGTH];
295 | for (int i = 0; i < signatureSource.getSecurity(); i++) {
296 | int[] digestBuffer = ISS.digest(mode, normalizedBundleFragments[i % 3], Arrays.copyOfRange(signatureTrits, i * ISS.FRAGMENT_LENGTH, (i + 1) * ISS.FRAGMENT_LENGTH));
297 | System.arraycopy(digestBuffer, 0, digests, i * HASH_LENGTH, HASH_LENGTH);
298 | }
299 | int[] addressTrits = ISS.address(mode, digests);
300 |
301 | int[] calculatedRootTrits = ISS.getMerkleRoot(mode, addressTrits, siblingsTrits,
302 | 0, index, siblingsTrits.length / HASH_LENGTH);
303 |
304 | if (!Arrays.equals(rootTrits, calculatedRootTrits)) {
305 | String msg = "Calculated root does not match expected! Aborting. " + root + " :: " + Converter.trytes(calculatedRootTrits);
306 | log.error(msg);
307 | throw new RuntimeException(msg);
308 | }
309 | }
310 |
311 | private void chainTransactionsFillSignatures(int mwm, List txs, String signature) {
312 | //to chain transactions we start from the LastIndex and move towards index 0.
313 | Collections.reverse(txs);
314 |
315 | txs.stream().skip(1).forEach(tx -> {
316 | //copy signature fragment
317 | String sigFragment = signature.substring((int) (tx.getCurrentIndex() * SIGNATURE_LENGTH),
318 | (int) (tx.getCurrentIndex() + 1) * SIGNATURE_LENGTH);
319 | tx.setSignatureFragments(sigFragment);
320 |
321 | //chain bundle
322 | String prevHash = txs.get((int) (tx.getLastIndex() - tx.getCurrentIndex() - 1)).getHash();
323 | tx.setTrunkTransaction(prevHash);
324 |
325 | //perform PoW
326 | Transaction tPoW = new Transaction(getPoWProvider().performPoW(tx.toTrytes(), mwm));
327 | tx.setAttachmentTimestamp(tPoW.getAttachmentTimestamp());
328 | tx.setAttachmentTimestampLowerBound(tPoW.getAttachmentTimestampLowerBound());
329 | tx.setAttachmentTimestampUpperBound(tPoW.getAttachmentTimestampUpperBound());
330 | tx.setNonce(tPoW.getNonce());
331 | tx.setHash(Hasher.hashTrytes(powMode, tx.toTrytes()));
332 | });
333 |
334 | Collections.reverse(txs);
335 | }
336 |
337 | private String calculateBundleHash(List txs) {
338 |
339 | ICurl sponge = SpongeFactory.create(SpongeFactory.Mode.KERL);
340 |
341 | for (Transaction tx : txs) {
342 | sponge.absorb(Converter.trits(tx.toTrytes().substring(OFFSET, OFFSET + LENGTH)));
343 | }
344 |
345 | int[] bundleHashTrits = new int[HASH_LENGTH];
346 | sponge.squeeze(bundleHashTrits, 0, HASH_LENGTH);
347 |
348 | return Converter.trytes(bundleHashTrits, 0, HASH_LENGTH);
349 | }
350 | }
351 |
--------------------------------------------------------------------------------
/compass/milestone/MilestoneSource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass;
27 |
28 | import com.google.common.base.Strings;
29 | import org.iota.jota.model.Transaction;
30 | import org.iota.jota.pow.SpongeFactory;
31 |
32 | import java.util.List;
33 |
34 | public abstract class MilestoneSource {
35 | public final static String EMPTY_HASH = Strings.repeat("9", 81);
36 | public final static String EMPTY_TAG = Strings.repeat("9", 27);
37 | public final static String EMPTY_MSG = Strings.repeat("9", 27 * 81);
38 |
39 | /**
40 | * @return the merkle tree root backed by this `MilestoneSource`
41 | */
42 | public abstract String getRoot();
43 |
44 | /**
45 | * @return the sponge mode used by this `MilestoneSource` for performing proof of work
46 | */
47 | public abstract SpongeFactory.Mode getPoWMode();
48 |
49 | public abstract List createMilestone(String trunk, String branch, int index, int mwm);
50 | }
51 |
--------------------------------------------------------------------------------
/compass/milestone/MilestoneTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass;
27 |
28 | import com.google.common.base.Strings;
29 | import org.iota.jota.model.Transaction;
30 | import org.iota.jota.pow.SpongeFactory;
31 | import org.iota.jota.utils.Converter;
32 | import org.iota.compass.conf.LayersCalculatorConfiguration;
33 | import org.iota.compass.conf.SignatureSourceServerConfiguration;
34 | import org.iota.compass.crypto.Hasher;
35 | import org.iota.compass.crypto.ISS;
36 | import org.junit.Assert;
37 | import org.junit.Test;
38 | import org.junit.runner.RunWith;
39 | import org.junit.runners.JUnit4;
40 |
41 | import java.io.IOException;
42 | import java.net.URL;
43 | import java.util.Arrays;
44 | import java.util.List;
45 | import java.util.Random;
46 | import java.util.stream.Collectors;
47 |
48 | import static org.iota.jota.pow.SpongeFactory.Mode.*;
49 |
50 | /**
51 | * Tests milestone generation & verifies the signatures
52 | */
53 | @RunWith(JUnit4.class)
54 | public class MilestoneTest {
55 |
56 | private void runForMode(SpongeFactory.Mode powMode, URL powHost, SignatureSource signatureSource) {
57 | final int depth = 4;
58 | final int MWM = 4;
59 |
60 | final SpongeFactory.Mode sigMode = signatureSource.getSignatureMode();
61 |
62 | final LayersCalculatorConfiguration layersConfig = new LayersCalculatorConfiguration();
63 | layersConfig.depth = depth;
64 | final LayersCalculator layersCalculator = new LayersCalculator(layersConfig, signatureSource);
65 |
66 | final List addresses = layersCalculator.calculateAllAddresses();
67 | final List> layers = layersCalculator.calculateAllLayers(addresses);
68 | final MilestoneDatabase db = new MilestoneDatabase(powMode, powHost, signatureSource, layers);
69 |
70 | for (int i = 0; i < (1 << depth); i++) {
71 | final List txs = db.createMilestone(TestUtil.nextSeed(), TestUtil.nextSeed(), i, MWM);
72 |
73 | final Transaction txFirst = txs.get(0);
74 | final Transaction txSiblings = txs.get(txs.size() - 1);
75 |
76 | txs.forEach(tx -> System.err.println(Hasher.hashTrytes(powMode, tx.toTrytes())));
77 |
78 | txs.forEach(tx -> Assert.assertTrue("Transaction PoW MWM not met",
79 | tx.getHash().endsWith(Strings.repeat("9", MWM / 3))));
80 | Assert.assertEquals(db.getRoot(), txFirst.getAddress());
81 | final int[] trunkTrits = ISS.normalizedBundle(Converter.trits(Hasher.hashTrytes(powMode, txSiblings.toTrytes())));
82 |
83 | // Get digest of each individual signature.
84 | int[] signatureTrits = Converter.trits(
85 | txs.stream()
86 | .limit(txs.size() - 1)
87 | .map(t -> Converter.trytes(ISS.digest(sigMode,
88 | Arrays.copyOfRange(trunkTrits, (int) t.getCurrentIndex() * ISS.NUMBER_OF_FRAGMENT_CHUNKS,
89 | (int) (t.getCurrentIndex() + 1) * ISS.NUMBER_OF_FRAGMENT_CHUNKS),
90 | Converter.trits(t.getSignatureFragments()))))
91 | .collect(Collectors.joining("")));
92 |
93 | int[] signatureAddress = ISS.address(sigMode, signatureTrits);
94 | Assert.assertEquals(addresses.get(i), Converter.trytes(signatureAddress));
95 |
96 | int[] siblingTrits = Converter.trits(txSiblings.getSignatureFragments());
97 | int[] root = ISS.getMerkleRoot(sigMode, signatureAddress, siblingTrits, 0, i, depth);
98 | Assert.assertEquals(db.getRoot(), Converter.trytes(root));
99 | }
100 | }
101 |
102 | @Test
103 | public void runRemoteTest() throws IOException {
104 | int port = new Random().nextInt(14436) + 51200;
105 |
106 | final String seed = TestUtil.nextSeed();
107 |
108 | SignatureSourceServerConfiguration config = new SignatureSourceServerConfiguration();
109 | config.port = port;
110 | config.security = 1;
111 | config.sigMode = SpongeFactory.Mode.CURLP27;
112 | config.plaintext = true;
113 | config.seed = seed;
114 |
115 | SignatureSourceServer server = new SignatureSourceServer(config);
116 | server.start();
117 |
118 | SignatureSource source = new RemoteSignatureSource("localhost:" + port);
119 |
120 | runForMode(SpongeFactory.Mode.CURLP81, null, source);
121 |
122 | server.stop();
123 | }
124 |
125 | @Test
126 | public void runTests() {
127 | int from = 1, to = 3;
128 | SpongeFactory.Mode[] powModes = new SpongeFactory.Mode[]{
129 | // Jota's LocalPoWProvider only supports CURLP81
130 | // CURLP27,
131 | CURLP81,
132 | KERL
133 | };
134 |
135 | SpongeFactory.Mode[] sigModes = new SpongeFactory.Mode[]{
136 | KERL,
137 | CURLP27,
138 | CURLP81
139 | };
140 |
141 | final String seed = TestUtil.nextSeed();
142 |
143 | for (SpongeFactory.Mode powMode : powModes) {
144 | for (SpongeFactory.Mode sigMode : sigModes) {
145 | for (int security = from; security <= to; security++) {
146 | SignatureSource source = new InMemorySignatureSource(sigMode, seed, security);
147 |
148 | System.err.println("Running: " + powMode + " : " + sigMode + " : " + security);
149 | runForMode(powMode, null, source);
150 | }
151 | }
152 | }
153 |
154 | }
155 |
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/compass/sign/BUILD:
--------------------------------------------------------------------------------
1 | MAIN_BASE_PATH = "src/main/java/org/iota/compass/%s"
2 |
3 | java_library(
4 | name = "inmemory",
5 | srcs = ["InMemorySignatureSource.java"],
6 | visibility = ["//visibility:public"],
7 | deps = [
8 | ":common",
9 | "//compass/crypto",
10 | "@org_iota_jota//jar",
11 | "@org_slf4j_slf4j_api//jar",
12 | ],
13 | )
14 |
15 | java_library(
16 | name = "remote",
17 | srcs = ["RemoteSignatureSource.java"],
18 | visibility = ["//visibility:public"],
19 | deps = [
20 | ":common",
21 | "//proto:signature_source_java_grpc",
22 | "//proto:signature_source_java_proto",
23 | "@com_google_api_grpc_proto_google_common_protos//jar",
24 | "@com_google_code_findbugs_jsr305//jar",
25 | "@com_google_guava_guava//jar",
26 | "@com_google_protobuf//:protobuf_java",
27 | "@com_google_protobuf//:protobuf_java_util",
28 | "@io_grpc_grpc_java//alts",
29 | "@io_grpc_grpc_java//core",
30 | "@io_grpc_grpc_java//netty",
31 | "@io_grpc_grpc_java//protobuf",
32 | "@io_grpc_grpc_java//stub",
33 | "@io_netty_netty_handler//jar",
34 | "@org_iota_jota//jar",
35 | "@org_slf4j_slf4j_api//jar",
36 | ],
37 | )
38 |
39 | java_library(
40 | name = "common",
41 | srcs = [
42 | "SignatureSource.java",
43 | "SignatureSourceType.java",
44 | ],
45 | visibility = ["//visibility:public"],
46 | deps = [
47 | "@org_iota_jota//jar",
48 | ],
49 | )
50 |
51 | java_library(
52 | name = "helper",
53 | srcs = [
54 | "SignatureSourceHelper.java",
55 | ],
56 | visibility = ["//visibility:public"],
57 | deps = [
58 | ":common",
59 | ":inmemory",
60 | ":remote",
61 | "//compass/conf",
62 | "@com_beust_jcommander//jar",
63 | ],
64 | )
65 |
66 | java_binary(
67 | name = "server",
68 | srcs = ["SignatureSourceServer.java"],
69 | main_class = "org.iota.compass.SignatureSourceServer",
70 | visibility = ["//visibility:public"],
71 | runtime_deps = ["@org_slf4j_slf4j_simple//jar"],
72 | deps = [
73 | ":common",
74 | ":inmemory",
75 | "//compass/conf",
76 | "//compass/crypto",
77 | "//proto:signature_source_java_grpc",
78 | "//proto:signature_source_java_proto",
79 | "@com_beust_jcommander//jar",
80 | "@com_google_api_grpc_proto_google_common_protos//jar",
81 | "@com_google_code_findbugs_jsr305//jar",
82 | "@com_google_guava_guava//jar",
83 | "@com_google_protobuf//:protobuf_java",
84 | "@com_google_protobuf//:protobuf_java_util",
85 | "@io_grpc_grpc_java//alts",
86 | "@io_grpc_grpc_java//core",
87 | "@io_grpc_grpc_java//netty",
88 | "@io_grpc_grpc_java//protobuf",
89 | "@io_grpc_grpc_java//stub",
90 | "@io_netty_netty_handler//jar",
91 | "@org_iota_jota//jar",
92 | "@org_slf4j_slf4j_api//jar",
93 | ],
94 | )
95 |
--------------------------------------------------------------------------------
/compass/sign/InMemorySignatureSource.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass;
2 |
3 |
4 | import org.iota.jota.pow.JCurl;
5 | import org.iota.jota.pow.SpongeFactory;
6 | import org.iota.jota.utils.Converter;
7 | import org.iota.compass.crypto.ISS;
8 | import org.iota.compass.crypto.ISSInPlace;
9 |
10 | import java.util.Arrays;
11 |
12 | /**
13 | * A signature provider that holds the seed in local memory.
14 | */
15 | public class InMemorySignatureSource extends SignatureSource {
16 |
17 | private final SpongeFactory.Mode mode;
18 | private final int[] seed;
19 | private final int security;
20 |
21 | public InMemorySignatureSource(SpongeFactory.Mode mode, String seed, int security) {
22 | this.mode = mode;
23 | this.seed = Converter.trits(seed);
24 | this.security = security;
25 | }
26 |
27 | @Override
28 | public int getSecurity() {
29 | return security;
30 | }
31 |
32 | @Override
33 | public SpongeFactory.Mode getSignatureMode() {
34 | return mode;
35 | }
36 |
37 | @Override
38 | public String getAddress(long index) {
39 | int[] subseed = new int[JCurl.HASH_LENGTH];
40 | int[] key = new int[ISSInPlace.FRAGMENT_LENGTH * security];
41 | int[] digests = new int[key.length / ISSInPlace.FRAGMENT_LENGTH * JCurl.HASH_LENGTH];
42 | int[] address = new int[JCurl.HASH_LENGTH];
43 |
44 | System.arraycopy(seed, 0, subseed, 0, subseed.length);
45 | ISSInPlace.subseed(mode, subseed, index);
46 | ISSInPlace.key(mode, subseed, key);
47 | Arrays.fill(subseed, 0);
48 | ISSInPlace.digests(mode, key, digests);
49 | Arrays.fill(key, 0);
50 | ISSInPlace.address(mode, digests, address);
51 |
52 | return Converter.trytes(address);
53 | }
54 |
55 | /**
56 | * @param index key / tree leaf index to generate signature for
57 | * @param hashToSign the hash to be signed
58 | * @return a valid signature for {@code hashToSign} using the {@code index} leaf
59 | */
60 | @Override
61 | public String getSignature(long index, String hashToSign) {
62 | int[] subseed = ISS.subseed(mode, seed, index);
63 | int[] key = ISS.key(mode, subseed, security);
64 | Arrays.fill(subseed, 0);
65 |
66 | int[] normalizedBundle = ISS.normalizedBundle(Converter.trits(hashToSign));
67 |
68 | StringBuilder fragment = new StringBuilder();
69 |
70 | for (int i = 0; i < getSecurity(); i++) {
71 | int[] curFrag = ISS.signatureFragment(getSignatureMode(),
72 | Arrays.copyOfRange(normalizedBundle, i * ISS.NUMBER_OF_FRAGMENT_CHUNKS, (i + 1) * ISS.NUMBER_OF_FRAGMENT_CHUNKS),
73 | Arrays.copyOfRange(key, i * ISS.FRAGMENT_LENGTH, (i + 1) * ISS.FRAGMENT_LENGTH));
74 | fragment.append(Converter.trytes(curFrag));
75 | }
76 |
77 | Arrays.fill(key, 0);
78 |
79 | return fragment.toString();
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/compass/sign/RemoteSignatureSource.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass;
2 |
3 | import io.grpc.ManagedChannelBuilder;
4 | import io.grpc.StatusRuntimeException;
5 | import io.grpc.netty.GrpcSslContexts;
6 | import io.grpc.netty.NettyChannelBuilder;
7 | import io.netty.handler.ssl.SslContext;
8 | import io.netty.handler.ssl.SslContextBuilder;
9 | import org.iota.jota.pow.SpongeFactory;
10 | import org.iota.compass.proto.*;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | import javax.net.ssl.SSLException;
15 | import java.io.File;
16 | import java.security.Security;
17 | import java.util.Optional;
18 | import java.util.concurrent.TimeUnit;
19 |
20 | /**
21 | * An implementation of a SignatureSource that talks to a remote gRPC service.
22 | */
23 | public class RemoteSignatureSource extends SignatureSource {
24 | private static final Logger log = LoggerFactory.getLogger(RemoteSignatureSource.class);
25 | public static final String DEFAULT_CACHE_TTL = "5";
26 |
27 | private SignatureSourceGrpc.SignatureSourceBlockingStub serviceStub;
28 | private final ManagedChannelBuilder channelBuilder;
29 |
30 | private Optional cachedSecurity = Optional.empty();
31 | private Optional cachedSignatureMode = Optional.empty();
32 |
33 | /**
34 | * Constructs a RemoteSignatureSource using an encrypted gRPC channel.
35 | *
36 | * @param uri the URI of the host to connect to
37 | * @param trustCertCollectionFilePath
38 | * @param clientCertChainFilePath
39 | * @param clientPrivateKeyFilePath
40 | * @throws SSLException
41 | */
42 | public RemoteSignatureSource(String uri,
43 | String trustCertCollectionFilePath,
44 | String clientCertChainFilePath,
45 | String clientPrivateKeyFilePath) throws SSLException {
46 |
47 | this.channelBuilder = createSecureManagedChannelBuilder(
48 | uri, trustCertCollectionFilePath, clientCertChainFilePath, clientPrivateKeyFilePath
49 | );
50 | this.serviceStub = SignatureSourceGrpc.newBlockingStub(channelBuilder.build());
51 |
52 | }
53 |
54 |
55 | /**
56 | * Constructs a RemoteSignatureSource using an *unencrypted* gRPC channel.
57 | *
58 | * @param uri the URI of the host to connect to
59 | */
60 | public RemoteSignatureSource(String uri) {
61 | this.channelBuilder = createPlaintextManagedChannelBuilder(uri);
62 | this.serviceStub = SignatureSourceGrpc.newBlockingStub(channelBuilder.build());
63 | }
64 |
65 | private ManagedChannelBuilder createSecureManagedChannelBuilder(String uri,
66 | String trustCertCollectionFilePath,
67 | String clientCertChainFilePath,
68 | String clientPrivateKeyFilePath) throws SSLException {
69 | String cacheTtl = Security.getProperty("networkaddress.cache.ttl");
70 | if (cacheTtl == null) {
71 | cacheTtl = DEFAULT_CACHE_TTL;
72 | }
73 | return NettyChannelBuilder
74 | .forTarget(uri)
75 | .idleTimeout(Integer.valueOf(cacheTtl) * 2, TimeUnit.SECONDS)
76 | .useTransportSecurity()
77 | .sslContext(
78 | buildSslContext(trustCertCollectionFilePath, clientCertChainFilePath, clientPrivateKeyFilePath)
79 | );
80 | }
81 |
82 | private ManagedChannelBuilder createPlaintextManagedChannelBuilder(String uri) {
83 | String cacheTtl = Security.getProperty("networkaddress.cache.ttl");
84 | if (cacheTtl == null) {
85 | cacheTtl = DEFAULT_CACHE_TTL;
86 | }
87 | return ManagedChannelBuilder
88 | .forTarget(uri)
89 | .idleTimeout(Integer.valueOf(cacheTtl) * 2, TimeUnit.SECONDS)
90 | .usePlaintext();
91 | }
92 |
93 | private static SslContext buildSslContext(
94 | String trustCertCollectionFilePath,
95 | String clientCertChainFilePath,
96 | String clientPrivateKeyFilePath) throws SSLException {
97 | SslContextBuilder builder = GrpcSslContexts.forClient();
98 | if (trustCertCollectionFilePath != null) {
99 | builder.trustManager(new File(trustCertCollectionFilePath));
100 | }
101 | if (clientCertChainFilePath != null && !clientCertChainFilePath.isEmpty()
102 | && clientPrivateKeyFilePath != null && !clientPrivateKeyFilePath.isEmpty()) {
103 | builder.keyManager(new File(clientCertChainFilePath), new File(clientPrivateKeyFilePath));
104 | }
105 | return builder.build();
106 | }
107 |
108 | @Override
109 | public String getSignature(long index, String hash) {
110 | log.trace("Requesting signature for index: " + index + " and hash: " + hash);
111 | GetSignatureResponse response;
112 | try {
113 | response = serviceStub.getSignature(GetSignatureRequest.newBuilder().setIndex(index).setHash(hash).build());
114 | } catch (StatusRuntimeException e) {
115 | // If an exception occurs, wait 10 seconds, and retry only once by rebuilding the gRPC client stub from a new Channel
116 | try {
117 | Thread.sleep(10_000);
118 | } catch (InterruptedException ex) {
119 | // Ignore the fact that we got interrupted
120 | }
121 | serviceStub = SignatureSourceGrpc.newBlockingStub(channelBuilder.build());
122 | response = serviceStub.getSignature(GetSignatureRequest.newBuilder().setIndex(index).setHash(hash).build());
123 | }
124 | return response.getSignature();
125 | }
126 |
127 | @Override
128 | public int getSecurity() {
129 | synchronized (cachedSecurity) {
130 | if (cachedSecurity.isPresent())
131 | return cachedSecurity.get();
132 |
133 |
134 | GetSecurityResponse response = serviceStub.getSecurity(GetSecurityRequest.getDefaultInstance());
135 | cachedSecurity = Optional.of(response.getSecurity());
136 |
137 | log.info("Caching security level: " + response.getSecurity());
138 |
139 | return response.getSecurity();
140 | }
141 | }
142 |
143 | @Override
144 | public SpongeFactory.Mode getSignatureMode() {
145 | synchronized (cachedSignatureMode) {
146 | if (cachedSignatureMode.isPresent()) return cachedSignatureMode.get();
147 |
148 | GetSignatureModeResponse response = serviceStub.getSignatureMode(GetSignatureModeRequest.getDefaultInstance());
149 |
150 | SpongeFactory.Mode spongeMode;
151 | switch (response.getMode()) {
152 | case CURLP27:
153 | spongeMode = SpongeFactory.Mode.CURLP27;
154 | break;
155 | case CURLP81:
156 | spongeMode = SpongeFactory.Mode.CURLP81;
157 | break;
158 | case KERL:
159 | spongeMode = SpongeFactory.Mode.KERL;
160 | break;
161 | default:
162 | throw new RuntimeException("Unknown remote signature mode: " + response.getMode());
163 | }
164 |
165 | cachedSignatureMode = Optional.of(spongeMode);
166 |
167 | log.info("Caching signature mode: " + spongeMode);
168 |
169 | return spongeMode;
170 | }
171 | }
172 |
173 | @Override
174 | public String getAddress(long index) {
175 | GetAddressResponse response = serviceStub.getAddress(GetAddressRequest.newBuilder().setIndex(index).build());
176 | return response.getAddress();
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/compass/sign/SignatureSource.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass;
2 |
3 | import org.iota.jota.pow.SpongeFactory;
4 |
5 | public abstract class SignatureSource {
6 | /**
7 | * Provides the signature for the given milestone index.
8 | *
9 | * @param index the key / leaf index
10 | * @param bundleHash the hash to sign
11 | * @return trit-array containing the key
12 | */
13 | public abstract String getSignature(long index, String bundleHash);
14 |
15 | /**
16 | * The security level of this key provider
17 | *
18 | * @return security level (1 to 3 inclusive)
19 | */
20 | public abstract int getSecurity();
21 |
22 | /**
23 | * @return the signature mode for this key
24 | */
25 | public abstract SpongeFactory.Mode getSignatureMode();
26 |
27 |
28 | /**
29 | * @param index the key / leaf index
30 | * @return the address for the given key / leaf index
31 | */
32 | public abstract String getAddress(long index);
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/compass/sign/SignatureSourceHelper.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass;
2 |
3 | import com.beust.jcommander.JCommander;
4 | import org.iota.compass.conf.InMemorySignatureSourceConfiguration;
5 | import org.iota.compass.conf.RemoteSignatureSourceConfiguration;
6 |
7 | import javax.net.ssl.SSLException;
8 |
9 | public class SignatureSourceHelper {
10 | public static SignatureSource signatureSourceFromArgs(SignatureSourceType type, String[] args) throws SSLException {
11 | switch (type) {
12 | case REMOTE: {
13 | RemoteSignatureSourceConfiguration sourceConf = new RemoteSignatureSourceConfiguration();
14 | JCommander.newBuilder().addObject(sourceConf).acceptUnknownOptions(true).build().parse(args);
15 |
16 | if (sourceConf.plaintext) {
17 | return new RemoteSignatureSource(sourceConf.uri);
18 | } else {
19 | return new RemoteSignatureSource(sourceConf.uri, sourceConf.trustCertCollection, sourceConf.clientCertChain, sourceConf.clientKey);
20 | }
21 | }
22 | case INMEMORY: {
23 | InMemorySignatureSourceConfiguration sourceConf = new InMemorySignatureSourceConfiguration();
24 | JCommander.newBuilder().addObject(sourceConf).acceptUnknownOptions(true).build().parse(args);
25 |
26 | return new InMemorySignatureSource(sourceConf.sigMode, sourceConf.seed, sourceConf.security);
27 | }
28 | }
29 |
30 | throw new IllegalArgumentException();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/compass/sign/SignatureSourceServer.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass;
2 |
3 | import com.beust.jcommander.JCommander;
4 | import io.grpc.Server;
5 | import io.grpc.netty.GrpcSslContexts;
6 | import io.grpc.netty.NettyServerBuilder;
7 | import io.grpc.stub.StreamObserver;
8 | import io.netty.handler.ssl.ClientAuth;
9 | import io.netty.handler.ssl.SslContextBuilder;
10 | import io.netty.handler.ssl.SslProvider;
11 | import org.iota.compass.conf.SignatureSourceServerConfiguration;
12 | import org.iota.compass.proto.*;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 |
16 | import java.io.File;
17 | import java.io.IOException;
18 |
19 | public class SignatureSourceServer {
20 | private static final Logger log = LoggerFactory.getLogger(SignatureSourceServer.class);
21 |
22 | private final SignatureSourceServerConfiguration config;
23 | private final SignatureSource signatureSource;
24 |
25 | private Server server;
26 |
27 | public SignatureSourceServer(SignatureSourceServerConfiguration config) {
28 | this.config = config;
29 | this.signatureSource = new InMemorySignatureSource(config.sigMode, config.seed, config.security);
30 | }
31 |
32 | public static void main(String[] args) throws IOException, InterruptedException {
33 | SignatureSourceServerConfiguration config = new SignatureSourceServerConfiguration();
34 |
35 | JCommander.newBuilder()
36 | .addObject(config)
37 | .build()
38 | .parse(args);
39 |
40 | final SignatureSourceServer server = new SignatureSourceServer(config);
41 | server.start();
42 | server.blockUntilShutdown();
43 | }
44 |
45 | public void start() throws IOException {
46 | NettyServerBuilder builder =
47 | NettyServerBuilder.forPort(config.port)
48 | .addService(new SignatureSourceImpl(signatureSource));
49 |
50 | if (!config.plaintext) {
51 | if (config.certChain == null || config.certChain.isEmpty()) {
52 | throw new IllegalArgumentException("-certChain is required if not running in plaintext mode");
53 | }
54 |
55 | if (config.privateKey == null || config.privateKey.isEmpty()) {
56 | throw new IllegalArgumentException("-privateKey is required if not running in plaintext mode");
57 | }
58 |
59 | SslContextBuilder sslClientContextBuilder = SslContextBuilder.forServer(new File(config.certChain),
60 | new File(config.privateKey));
61 | if (config.trustCertCollection != null) {
62 | sslClientContextBuilder.trustManager(new File(config.trustCertCollection));
63 | sslClientContextBuilder.clientAuth(ClientAuth.REQUIRE);
64 | }
65 |
66 | builder = builder.sslContext(GrpcSslContexts.configure(sslClientContextBuilder,
67 | SslProvider.OPENSSL).build());
68 | }
69 |
70 | server = builder.build();
71 | server.start();
72 |
73 | log.info("Server started, listening on " + config.port);
74 |
75 | Runtime.getRuntime().addShutdownHook(new Thread(() -> {
76 | System.err.println("*** shutting down gRPC server since JVM is shutting down");
77 | SignatureSourceServer.this.stop();
78 | System.err.println("*** server shut down");
79 | }));
80 | }
81 |
82 | public void stop() {
83 | if (server != null) {
84 | server.shutdown();
85 | }
86 | }
87 |
88 | public void blockUntilShutdown() throws InterruptedException {
89 | if (server != null) {
90 | server.awaitTermination();
91 | }
92 | }
93 |
94 |
95 | static class SignatureSourceImpl extends SignatureSourceGrpc.SignatureSourceImplBase {
96 | private final SignatureSource signatureSource;
97 |
98 | public SignatureSourceImpl(SignatureSource signatureSource) {
99 | super();
100 | this.signatureSource = signatureSource;
101 | }
102 |
103 | @Override
104 | public void getSecurity(GetSecurityRequest request, StreamObserver responseObserver) {
105 | log.info("Responding to getSecurity");
106 | responseObserver.onNext(GetSecurityResponse.newBuilder()
107 | .setSecurity(signatureSource.getSecurity())
108 | .build());
109 | responseObserver.onCompleted();
110 | }
111 |
112 | @Override
113 | public void getSignatureMode(GetSignatureModeRequest request, StreamObserver responseObserver) {
114 | log.info("Responding to getSignatureMode");
115 | SignatureMode mode;
116 |
117 | switch (signatureSource.getSignatureMode()) {
118 | case CURLP27:
119 | mode = SignatureMode.CURLP27;
120 | break;
121 | case CURLP81:
122 | mode = SignatureMode.CURLP81;
123 | break;
124 | case KERL:
125 | mode = SignatureMode.KERL;
126 | break;
127 | default:
128 | throw new RuntimeException();
129 | }
130 |
131 | responseObserver.onNext(GetSignatureModeResponse.newBuilder()
132 | .setMode(mode)
133 | .build());
134 | responseObserver.onCompleted();
135 | }
136 |
137 | @Override
138 | public void getSignature(GetSignatureRequest request, StreamObserver responseObserver) {
139 | log.info("Responding to getSignature for index: " + request.getIndex() + " and hash: " + request.getHash());
140 |
141 | responseObserver.onNext(GetSignatureResponse.newBuilder()
142 | .setSignature(signatureSource.getSignature(request.getIndex(), request.getHash()))
143 | .build());
144 | responseObserver.onCompleted();
145 | }
146 |
147 | @Override
148 | public void getAddress(GetAddressRequest request, StreamObserver responseObserver) {
149 | log.info("Responding to getAddress for index: " + request.getIndex());
150 |
151 | responseObserver.onNext(GetAddressResponse.newBuilder()
152 | .setAddress(signatureSource.getAddress(request.getIndex()))
153 | .build());
154 | responseObserver.onCompleted();
155 | }
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/compass/sign/SignatureSourceType.java:
--------------------------------------------------------------------------------
1 | package org.iota.compass;
2 |
3 | public enum SignatureSourceType {
4 | INMEMORY, REMOTE
5 | }
6 |
--------------------------------------------------------------------------------
/compass/simplelogger.properties:
--------------------------------------------------------------------------------
1 | #
2 | # This file is part of TestnetCOO.
3 | #
4 | # Copyright (C) 2018 IOTA Stiftung
5 | # TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | #
7 | # TestnetCOO is free software: you can redistribute it and/or modify
8 | # it under the terms of the GNU Affero General Public License as published
9 | # by the Free Software Foundation, either version 3 of the License,
10 | # or (at your option) any later version.
11 | #
12 | # TestnetCOO is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU Affero General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU Affero General Public License
18 | # along with TestnetCOO. If not, see:
19 | # http://www.gnu.org/licenses/
20 | #
21 | # For more information contact:
22 | # IOTA Stiftung
23 | # https://www.iota.org/
24 | #
25 |
26 | org.slf4j.simpleLogger.defaultLogLevel=info
27 | #org.slf4j.simpleLogger.log.xxxxx=
28 |
29 | org.slf4j.simpleLogger.showDateTime=true
30 | org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z
31 |
32 | org.slf4j.simpleLogger.showThreadName=true
33 | org.slf4j.simpleLogger.showLogName=true
34 |
--------------------------------------------------------------------------------
/compass/test/BUILD:
--------------------------------------------------------------------------------
1 | java_library(
2 | name = "test",
3 | srcs = ["TestUtil.java"],
4 | visibility = ["//visibility:public"],
5 | )
6 |
--------------------------------------------------------------------------------
/compass/test/TestUtil.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of TestnetCOO.
3 | *
4 | * Copyright (C) 2018 IOTA Stiftung
5 | * TestnetCOO is Copyright (C) 2017-2018 IOTA Stiftung
6 | *
7 | * TestnetCOO is free software: you can redistribute it and/or modify
8 | * it under the terms of the GNU Affero General Public License as published
9 | * by the Free Software Foundation, either version 3 of the License,
10 | * or (at your option) any later version.
11 | *
12 | * TestnetCOO is distributed in the hope that it will be useful,
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | * GNU Affero General Public License for more details.
16 | *
17 | * You should have received a copy of the GNU Affero General Public License
18 | * along with TestnetCOO. If not, see:
19 | * http://www.gnu.org/licenses/
20 | *
21 | * For more information contact:
22 | * IOTA Stiftung
23 | * https://www.iota.org/
24 | */
25 |
26 | package org.iota.compass;
27 |
28 | import java.security.SecureRandom;
29 | import java.util.Random;
30 |
31 | public class TestUtil {
32 |
33 | private static final String ALPHABET = "9ABCDEFGHIJKLMNOPQRSTUVWXYZ";
34 | private static final int randomnessSeed = 1;
35 | private static final Random random = new SecureRandom();
36 |
37 | {
38 | //for deterministic testing
39 | random.setSeed(randomnessSeed);
40 | }
41 |
42 | static String nextSeed() {
43 | return nextTrytes(81);
44 | }
45 |
46 | private static String nextTrytes(int count) {
47 | char[] buf = new char[count];
48 |
49 | for (int idx = 0; idx < buf.length; ++idx)
50 | buf[idx] = ALPHABET.charAt(random.nextInt(ALPHABET.length()));
51 |
52 | return new String(buf);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/docker/BUILD:
--------------------------------------------------------------------------------
1 | load("@io_bazel_rules_docker//container:container.bzl", "container_image")
2 |
3 | container_image(
4 | name = "layers_calculator",
5 | base = "@java_base//image",
6 | cmd = ["layers_calculator_deploy.jar"],
7 | files = ["//compass:layers_calculator_deploy.jar"],
8 | repository = "iota/compass",
9 | )
10 |
11 | container_image(
12 | name = "coordinator",
13 | base = "@java_base//image",
14 | cmd = ["coordinator_deploy.jar"],
15 | files = ["//compass:coordinator_deploy.jar"],
16 | repository = "iota/compass",
17 | )
18 |
19 | container_image(
20 | name = "shadowing_coordinator",
21 | base = "@java_base//image",
22 | cmd = ["shadowing_coordinator_deploy.jar"],
23 | files = ["//compass:shadowing_coordinator_deploy.jar"],
24 | repository = "iota/compass",
25 | )
26 |
27 | container_image(
28 | name = "signature_source_server",
29 | base = "@java_base//image",
30 | cmd = ["server_deploy.jar"],
31 | files = ["//compass/sign:server_deploy.jar"],
32 | repository = "iota/compass",
33 | )
34 |
--------------------------------------------------------------------------------
/docs/HOWTO_private_tangle.md:
--------------------------------------------------------------------------------
1 | # HOWTO: Setting up a private Tangle
2 |
3 | ## Introduction
4 | A private Tangle consists in a set of IRI nodes interconnected between each other. We also recommended you create Private Tangles with their own genesis (a custom snapshot file). This will clearly differentiate your Private Tangle from any other existing network, whether public or private.
5 |
6 | A private Tangle can consist of a single IRI instance. The instructions below will guide you through creating your own single node private Tangle.
7 |
8 | ### The components
9 | - IRI — IOTA Reference Implementation — software
10 | - A custom snapshot file — genesis
11 | - The Coordinator (COO)
12 | - The scripts in `docs/private_tangle`
13 |
14 | ## Important things to note
15 | The instructions below do not consider all individual circumstances of your setup. They are meant to give you an understanding on how to bootstrap your own Private Tangle on a 1 node network topology. More complex setups can be achieved by running more IRI nodes interconnected between each other.
16 |
17 | If you prefer to use Docker to set up IRI instances, we provide [IRI docker containers](https://hub.docker.com/r/iotaledger/iri/). We recommend adapting the instructions below by following the [IRI Docker instructions](https://github.com/iotaledger/iri/blob/dev/DOCKER.md).
18 |
19 | ## Step 1: Setting up the Coordinator
20 | The Coordinator uses Java to run. These instructions assume that you have already setup [bazel](https://bazel.build) on
21 | your system and installed the `//docker:coordinator` and `//docker:layers_calculator` images. The relevant scripts are inside the `private_tangle` folder.
22 | **The scripts assume that they are in the same folder as the `config.json` file and data folders.**
23 |
24 | ### Bootstrapping the Coordinator
25 | We now need to bootstrap the Coordinator milestone merkle tree.
26 | 1. Generate a valid random seed.
27 | The seed is going to be used by the COO to generate and sign milestones. **Do not lose the generated seed.**
28 |
29 | ```
30 | cat /dev/urandom |LC_ALL=C tr -dc 'A-Z9' | fold -w 81 | head -n 1
31 | ```
32 |
33 | The output of the command above will be a random string of 81 chars, all capital letters, such as this:
34 | `COOSEED99999999999999999999999999999999999999999999999999999999999999999999999999`.
35 |
36 | 2. Decide on the depth of the Coordinator.
37 |
38 | The higher the number, the more milestones can be issued: At depth 18, = ~260 thousand milestones,
39 | 20 = ~1 million milestones, 21 = ~2 million milestones – or more precisely 2^DEPTH.
40 |
41 | For this exercise, we use depth 8 — allowing 256 milestones to be issued.
42 |
43 | **Keep in mind this process is highly CPU intensive. For example, generating a depth 20 tree on a 64 CPU server takes about 1 hour.**
44 | 3. Copy the `config.example.json` file to `config.json` and alter its contents (specifying correct depth & seed).
45 | 4. Run the layer calculator via `./01_calculate_layers.sh` from the `private_tangle` folder.
46 | 5. After completing execution, the LayersCalculator will tell you the root of the generated merkle tree. *This is the Coordinator's address*.
47 |
48 | ## Step 2: Running the IRI node
49 | IRI is an open source, Java reference implementation of the IOTA protocol. The development of IRI is supported by the IOTA Foundation.
50 |
51 | Dedicate a Linux server as an IRI node. The server requirements are low, we recommend the following for a better experience:
52 |
53 | - VPS or bare metal
54 | - 4 CPUs (or virtual CPUs)
55 | - 8GB RAM
56 | - SSD drive with at least 10GB – highly dependent on how much data you wish to store
57 | - Virtually any Linux distribution, as long as Docker is available. We recommend Ubuntu Linux and this guide assumes it’s Ubuntu Linux.
58 |
59 | The script assumes that the DB will be stored in the same path as the script.
60 |
61 | If you look inside the script, here's some of those parameters explaned:
62 |
63 | - `--testnet-coordinator $COO_ADDRESS` - the Coordinator address that IRI listens on
64 | - `--mwm` (e.g. `9`) - sets the minimum weight magnitude (MWM) required by a client when performing proof-of-work (PoW). Keep in mind that an MWM of 9 requires a negligible amount of PoW. For comparison, the IOTA Mainnet Network uses `MWM = 14`.
65 | - `--max-depth` (e.g. `1000`) - only required on the node where the COO will be issuing milestones. If you are creating more than one IRI node, this is not necessary.
66 | - `--milestone-start` (e.g. `1`) - the lowest milestone index that IRI uses
67 | - `--milestone-keys` - see the description of `depth` further above
68 | - `--snapshot` - the file containing the private tangle's current snapshot information
69 |
70 | ### Create custom genesis
71 | Create a custom genesis `snapshot.txt` file and place it in the same folder as the script.
72 |
73 | Here's an **example** one:
74 | ```yaml
75 | FJHSSHBZTAKQNDTIKJYCZBOZDGSZANCZSWCNWUOCZXFADNOQSYAHEJPXRLOVPNOQFQXXGEGVDGICLMOXX;2779530283277761
76 | ```
77 | This allocates all token supply to seed `SEED99999999999999999999999999999999999999999999999999999999999999999999999999999`
78 |
79 | ### Start IRI
80 | ```
81 | ./02_run_iri.sh
82 | ```
83 |
84 | ### IRI node explained
85 | IRI by default uses three ports. If you need to access these ports remotely, please make sure the firewall on your server is setup accordingly. The ports are:
86 |
87 | - UDP neighbor peering port (default is `14600`)
88 | - TCP neighbor peering port (default is `15600`)
89 | - TCP HTTP API port (default is `14265`)
90 |
91 | #### Checking the Node Status
92 | Using curl and jq you can test the TCP API port.
93 |
94 | ```
95 | apt-get install -y curl jq
96 | curl -s http://localhost:14265 -X POST -H 'X-IOTA-API-Version: 1' -H 'Content-Type: application/json' -d '{"command": "getNodeInfo"}' | jq
97 | ```
98 |
99 | Please refer to https://iota.readme.io/reference for all HTTP IRI API commands available.
100 |
101 | ## Step 3: Running the Coordinator
102 | The IRI node is now running but it has not received its first milestone. We need to bootstrap the Tangle.
103 | We suggest at this stage to have two terminals open on your server. One with journalctl or equivalent looking at its logs and one running the following commands:
104 |
105 | ```
106 | ./03_run_coordinator.sh -bootstrap -broadcast
107 | ```
108 |
109 | Let this command run until you see output similar to:
110 | ```
111 | 07/20 09:10:43.699 [Latest Milestone Tracker] INFO com.iota.iri.Milestone - Latest milestone has changed from #2 to #3
112 | 07/20 09:10:45.385 [Solid Milestone Tracker] INFO com.iota.iri.Milestone - Latest SOLID SUBTANGLE milestone has changed from #2 to #3
113 | ```
114 |
115 | For future runs, you no longer need to provide the `-bootstrap` parameter (the Coordinator actually won't start with it).
116 | The `-broadcast` flag, however, is required as a security measure that the Coordinator should actually broadcast its milestones to IRI.
117 |
118 | A new milestone will be issued by the COO every 60 seconds (set by `"tick": 60000` in the `config.json`).
119 |
120 | You now have a working Private Tangle.
121 |
--------------------------------------------------------------------------------
/docs/private_tangle/.gitignore:
--------------------------------------------------------------------------------
1 | config.json
2 | snapshot.txt
3 | data/
4 | db/
5 | .*.swp
6 |
--------------------------------------------------------------------------------
/docs/private_tangle/01_calculate_layers.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | scriptdir=$(dirname "$(readlink -f "$0")")
4 | . $scriptdir/lib.sh
5 |
6 | load_config
7 |
8 | docker run -t --rm -v $scriptdir/data:/data iota/compass/docker:layers_calculator layers_calculator_deploy.jar -sigMode $sigMode -seed $seed -depth $depth -security $security -layers /data/layers
9 |
--------------------------------------------------------------------------------
/docs/private_tangle/02_run_iri.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | scriptdir=$(dirname "$(readlink -f "$0")")
4 | . $scriptdir/lib.sh
5 |
6 | load_config
7 |
8 | COO_ADDRESS=$(cat $scriptdir/data/layers/layer.0.csv)
9 |
10 | docker pull iotaledger/iri:latest
11 | docker run -t --net host --rm -v $scriptdir/db:/iri/data -v $scriptdir/snapshot.txt:/snapshot.txt -p 14265 iotaledger/iri:latest \
12 | --testnet true \
13 | --remote true \
14 | --testnet-coordinator $COO_ADDRESS \
15 | --testnet-coordinator-security-level $security \
16 | --testnet-coordinator-signature-mode $sigMode \
17 | --mwm $mwm \
18 | --milestone-start $milestoneStart \
19 | --milestone-keys $depth \
20 | --snapshot /snapshot.txt \
21 | --max-depth 1000 $@
22 |
23 |
--------------------------------------------------------------------------------
/docs/private_tangle/03_run_coordinator.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | scriptdir=$(dirname "$(readlink -f "$0")")
4 | . $scriptdir/lib.sh
5 |
6 | load_config
7 |
8 | docker run -t --net host --rm -v $scriptdir/data:/data iota/compass/docker:coordinator coordinator_deploy.jar \
9 | -layers /data/layers \
10 | -statePath /data/compass.state \
11 | -sigMode $sigMode \
12 | -powMode $powMode \
13 | -mwm $mwm \
14 | -security $security \
15 | -seed $seed \
16 | -tick $tick \
17 | -host $host \
18 | "$@"
19 |
--------------------------------------------------------------------------------
/docs/private_tangle/11_run_signature_source_server.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | scriptdir=$(dirname "$(readlink -f "$0")")
4 | . $scriptdir/lib.sh
5 |
6 | load_config
7 |
8 | docker run -t --net host --rm -v $scriptdir/data:/data iota/compass/docker:signature_source_server server_deploy.jar \
9 | -sigMode $sigMode \
10 | -security $security \
11 | -seed $seed \
12 | -plaintext \
13 | -port 50051 \
14 | "$@"
15 |
--------------------------------------------------------------------------------
/docs/private_tangle/12_run_coordinator_remote.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | scriptdir=$(dirname "$(readlink -f "$0")")
4 | . $scriptdir/lib.sh
5 |
6 | load_config
7 |
8 | docker run -t --net host --rm -v $scriptdir/data:/data iota/compass/docker:coordinator coordinator_deploy.jar \
9 | -layers /data/layers \
10 | -statePath /data/compass.state \
11 | -powMode $powMode \
12 | -mwm $mwm \
13 | -tick $tick \
14 | -host $host \
15 | -signatureSource remote \
16 | -remoteURI localhost:50051 \
17 | -remotePlaintext \
18 | "$@"
19 |
--------------------------------------------------------------------------------
/docs/private_tangle/21_calculate_layers_remote.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | scriptdir=$(dirname "$(readlink -f "$0")")
4 | . $scriptdir/lib.sh
5 |
6 | load_config
7 |
8 | docker run -t --net host --rm -v $scriptdir/data:/data iota/compass/docker:layers_calculator layers_calculator_deploy.jar -depth $depth -layers /data/layers \
9 | -signatureSource remote \
10 | -remoteURI localhost:50051 \
11 | -remotePlaintext \
12 |
13 |
--------------------------------------------------------------------------------
/docs/private_tangle/config.example.json:
--------------------------------------------------------------------------------
1 | {
2 | "seed": "MYSEEDHEREPLEASEREPLACEMEIMMEDIATELYWITHSOMETHINGSECURE99999999999999999999999999",
3 | "powMode": "CURLP81",
4 | "sigMode": "CURLP27",
5 | "security": 1,
6 | "depth": 8,
7 | "milestoneStart": 0,
8 | "mwm": 9,
9 | "tick": 60000,
10 | "host": "http://localhost:14265"
11 | }
12 |
--------------------------------------------------------------------------------
/docs/private_tangle/lib.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | function load_config {
4 | if [ ! -f $scriptdir/config.json ]; then
5 | echo "Config file 'config.json' does not exist! Please look at config.example.json and create one!"
6 | exit 1
7 | fi
8 |
9 | if [ ! -d $scriptdir/data/ ]; then
10 | mkdir $scriptdir/data/
11 | echo "Depending on OS you might have to set SELinux permissions for data/"
12 | fi
13 |
14 | if [ ! -d $scriptdir/db/ ]; then
15 | mkdir $scriptdir/db/
16 | echo "Depending on OS you might have to set SELinux permissions for db/"
17 | fi
18 |
19 | mkdir $scriptdir/data &> /dev/null
20 | mkdir $scriptdir/db &> /dev/null
21 |
22 | host=$(jq -r .host $scriptdir/config.json)
23 | sigMode=$(jq -r .sigMode $scriptdir/config.json)
24 | powMode=$(jq -r .powMode $scriptdir/config.json)
25 | seed=$(jq -r .seed $scriptdir/config.json)
26 | security=$(jq .security $scriptdir/config.json)
27 | depth=$(jq .depth $scriptdir/config.json)
28 | tick=$(jq .tick $scriptdir/config.json)
29 | mwm=$(jq .mwm $scriptdir/config.json)
30 | milestoneStart=$(jq .milestoneStart $scriptdir/config.json)
31 | }
32 |
--------------------------------------------------------------------------------
/docs/private_tangle/snapshot.example.txt:
--------------------------------------------------------------------------------
1 | FJHSSHBZTAKQNDTIKJYCZBOZDGSZANCZSWCNWUOCZXFADNOQSYAHEJPXRLOVPNOQFQXXGEGVDGICLMOXX;2779530283277761
2 |
--------------------------------------------------------------------------------
/proto/BUILD:
--------------------------------------------------------------------------------
1 | load("@io_grpc_grpc_java//:java_grpc_library.bzl", "java_grpc_library")
2 |
3 | java_proto_library(
4 | name = "signature_source_java_proto",
5 | visibility = ["//visibility:public"],
6 | deps = [":signature_source_proto"],
7 | )
8 |
9 | java_grpc_library(
10 | name = "signature_source_java_grpc",
11 | srcs = [":signature_source_proto"],
12 | visibility = ["//visibility:public"],
13 | deps = [":signature_source_java_proto"],
14 | )
15 |
16 | proto_library(
17 | name = "signature_source_proto",
18 | srcs = ["signature_source.proto"],
19 | visibility = ["//visibility:public"],
20 | )
21 |
--------------------------------------------------------------------------------
/proto/signature_source.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | option java_multiple_files = true;
4 | option java_package = "org.iota.compass.proto";
5 | option java_outer_classname = "SignatureSourceProto";
6 |
7 | package org.iota.compass.proto;
8 |
9 | enum SignatureMode {
10 | CURLP27 = 0;
11 | CURLP81 = 1;
12 | KERL = 2;
13 | }
14 |
15 | message GetSignatureRequest {
16 | uint64 index = 1;
17 | string hash = 2;
18 | }
19 |
20 | message GetSignatureResponse {
21 | string signature = 1;
22 | }
23 |
24 | message GetSecurityRequest {
25 | }
26 | message GetSecurityResponse {
27 | uint32 security = 1;
28 | }
29 |
30 | message GetSignatureModeRequest {
31 | }
32 |
33 | message GetSignatureModeResponse {
34 | SignatureMode mode = 1;
35 | }
36 |
37 | message GetAddressRequest {
38 | uint64 index = 1;
39 | }
40 | message GetAddressResponse {
41 | string address = 1;
42 | }
43 |
44 | service SignatureSource {
45 | rpc GetSecurity (GetSecurityRequest) returns (GetSecurityResponse);
46 | rpc GetSignatureMode (GetSignatureModeRequest) returns (GetSignatureModeResponse);
47 | rpc GetSignature (GetSignatureRequest) returns (GetSignatureResponse);
48 | // Note that implementation of this method is optional.
49 | rpc GetAddress (GetAddressRequest) returns (GetAddressResponse);
50 | }
51 |
--------------------------------------------------------------------------------
/third-party/BUILD:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iotaledger-archive/compass/d01fbc401efa1cc4de23c83352ec9c1130d6049e/third-party/BUILD
--------------------------------------------------------------------------------
/third-party/maven_deps.bzl:
--------------------------------------------------------------------------------
1 | def maven_jars():
2 | # com.google.guava:guava:bundle:26.0-jre
3 | native.maven_jar(
4 | name = "com_google_code_findbugs_jsr305",
5 | artifact = "com.google.code.findbugs:jsr305:3.0.2",
6 | repository = "https://jcenter.bintray.com/",
7 | sha1 = "25ea2e8b0c338a877313bd4672d3fe056ea78f0d",
8 | )
9 |
10 | # com.google.guava:guava:bundle:26.0-jre
11 | native.maven_jar(
12 | name = "org_codehaus_mojo_animal_sniffer_annotations",
13 | artifact = "org.codehaus.mojo:animal-sniffer-annotations:1.14",
14 | repository = "https://jcenter.bintray.com/",
15 | sha1 = "775b7e22fb10026eed3f86e8dc556dfafe35f2d5",
16 | )
17 |
18 | # org.slf4j:slf4j-simple:jar:1.7.25
19 | native.maven_jar(
20 | name = "org_slf4j_slf4j_api",
21 | artifact = "org.slf4j:slf4j-api:1.7.25",
22 | repository = "https://jcenter.bintray.com/",
23 | sha1 = "da76ca59f6a57ee3102f8f9bd9cee742973efa8a",
24 | )
25 |
26 | # junit:junit:jar:4.12
27 | native.maven_jar(
28 | name = "org_hamcrest_hamcrest_core",
29 | artifact = "org.hamcrest:hamcrest-core:1.3",
30 | repository = "https://jcenter.bintray.com/",
31 | sha1 = "42a25dc3219429f0e5d060061f71acb49bf010a0",
32 | )
33 |
34 | native.maven_jar(
35 | name = "com_squareup_retrofit2_converter_gson",
36 | artifact = "com.squareup.retrofit2:converter-gson:2.4.0",
37 | repository = "https://jcenter.bintray.com/",
38 | sha1 = "15d7790ee311d961379c51b00aba12d5967cb7ea",
39 | )
40 |
41 | # com.squareup.retrofit2:converter-gson:jar:2.4.0
42 | native.maven_jar(
43 | name = "com_google_code_gson_gson",
44 | artifact = "com.google.code.gson:gson:2.8.2",
45 | repository = "https://jcenter.bintray.com/",
46 | sha1 = "3edcfe49d2c6053a70a2a47e4e1c2f94998a49cf",
47 | )
48 |
49 | native.maven_jar(
50 | name = "com_squareup_retrofit2_retrofit",
51 | artifact = "com.squareup.retrofit2:retrofit:2.4.0",
52 | repository = "https://jcenter.bintray.com/",
53 | sha1 = "fc4aa382632bfaa7be7b41579efba41d5a71ecf3",
54 | )
55 |
56 | # com.google.guava:guava:bundle:26.0-jre
57 | native.maven_jar(
58 | name = "org_checkerframework_checker_qual",
59 | artifact = "org.checkerframework:checker-qual:2.5.2",
60 | repository = "https://jcenter.bintray.com/",
61 | sha1 = "cea74543d5904a30861a61b4643a5f2bb372efc4",
62 | )
63 |
64 | # com.squareup.retrofit2:retrofit:jar:2.4.0
65 | native.maven_jar(
66 | name = "com_squareup_okhttp3_okhttp",
67 | artifact = "com.squareup.okhttp3:okhttp:3.10.0",
68 | repository = "https://jcenter.bintray.com/",
69 | sha1 = "7ef0f1d95bf4c0b3ba30bbae25e0e562b05cf75e",
70 | )
71 |
72 | # com.google.guava:guava:bundle:26.0-jre
73 | native.maven_jar(
74 | name = "com_google_errorprone_error_prone_annotations",
75 | artifact = "com.google.errorprone:error_prone_annotations:2.1.3",
76 | repository = "https://jcenter.bintray.com/",
77 | sha1 = "39b109f2cd352b2d71b52a3b5a1a9850e1dc304b",
78 | )
79 |
80 | native.maven_jar(
81 | name = "org_apache_commons_commons_lang3",
82 | artifact = "org.apache.commons:commons-lang3:3.8.1",
83 | repository = "https://jcenter.bintray.com/",
84 | sha1 = "6505a72a097d9270f7a9e7bf42c4238283247755",
85 | )
86 |
87 | # com.squareup.okhttp3:okhttp:jar:3.10.0
88 | native.maven_jar(
89 | name = "com_squareup_okio_okio",
90 | artifact = "com.squareup.okio:okio:1.14.0",
91 | repository = "https://jcenter.bintray.com/",
92 | sha1 = "102d7be47241d781ef95f1581d414b0943053130",
93 | )
94 |
95 | native.maven_jar(
96 | name = "com_google_guava_guava",
97 | artifact = "com.google.guava:guava:26.0-jre",
98 | repository = "https://jcenter.bintray.com/",
99 | sha1 = "6a806eff209f36f635f943e16d97491f00f6bfab",
100 | )
101 |
102 | native.maven_jar(
103 | name = "org_bouncycastle_bcprov_jdk15on",
104 | artifact = "org.bouncycastle:bcprov-jdk15on:1.60",
105 | repository = "https://jcenter.bintray.com/",
106 | sha1 = "bd47ad3bd14b8e82595c7adaa143501e60842a84",
107 | )
108 |
109 | native.maven_jar(
110 | name = "com_beust_jcommander",
111 | artifact = "com.beust:jcommander:1.72",
112 | repository = "https://jcenter.bintray.com/",
113 | sha1 = "6375e521c1e11d6563d4f25a07ce124ccf8cd171",
114 | )
115 |
116 | native.maven_jar(
117 | name = "org_slf4j_slf4j_simple",
118 | artifact = "org.slf4j:slf4j-simple:1.7.25",
119 | repository = "https://jcenter.bintray.com/",
120 | sha1 = "8dacf9514f0c707cbbcdd6fd699e8940d42fb54e",
121 | )
122 |
123 | native.maven_jar(
124 | name = "junit_junit",
125 | artifact = "junit:junit:4.12",
126 | repository = "https://jcenter.bintray.com/",
127 | sha1 = "2973d150c0dc1fefe998f834810d68f278ea58ec",
128 | )
129 |
130 | # com.google.guava:guava:bundle:26.0-jre
131 | native.maven_jar(
132 | name = "com_google_j2objc_j2objc_annotations",
133 | artifact = "com.google.j2objc:j2objc-annotations:1.1",
134 | repository = "https://jcenter.bintray.com/",
135 | sha1 = "ed28ded51a8b1c6b112568def5f4b455e6809019",
136 | )
137 |
138 | # org.iota:jota:1.0.0-beta6
139 | native.maven_jar(
140 | name = "org_iota_jota",
141 | artifact = "org.iota:jota:1.0.0-beta6",
142 | repository = "https://jcenter.bintray.com/",
143 | sha1 = "5a4141395af7b307f4a5cd89e87988ccce974fd0",
144 | )
145 |
146 | def maven_libraries():
147 | native.java_library(
148 | name = "com_google_code_findbugs_jsr305",
149 | visibility = ["//visibility:public"],
150 | exports = ["@com_google_code_findbugs_jsr305//jar"],
151 | )
152 |
153 | native.java_library(
154 | name = "org_codehaus_mojo_animal_sniffer_annotations",
155 | visibility = ["//visibility:public"],
156 | exports = ["@org_codehaus_mojo_animal_sniffer_annotations//jar"],
157 | )
158 |
159 | native.java_library(
160 | name = "org_slf4j_slf4j_api",
161 | visibility = ["//visibility:public"],
162 | exports = ["@org_slf4j_slf4j_api//jar"],
163 | )
164 |
165 | native.java_library(
166 | name = "org_hamcrest_hamcrest_core",
167 | visibility = ["//visibility:public"],
168 | exports = ["@org_hamcrest_hamcrest_core//jar"],
169 | )
170 |
171 | native.java_library(
172 | name = "com_squareup_retrofit2_converter_gson",
173 | visibility = ["//visibility:public"],
174 | exports = ["@com_squareup_retrofit2_converter_gson//jar"],
175 | runtime_deps = [
176 | ":com_google_code_gson_gson",
177 | ":com_squareup_retrofit2_retrofit",
178 | ],
179 | )
180 |
181 | native.java_library(
182 | name = "com_google_code_gson_gson",
183 | visibility = ["//visibility:public"],
184 | exports = ["@com_google_code_gson_gson//jar"],
185 | )
186 |
187 | native.java_library(
188 | name = "com_squareup_retrofit2_retrofit",
189 | visibility = ["//visibility:public"],
190 | exports = ["@com_squareup_retrofit2_retrofit//jar"],
191 | runtime_deps = [
192 | ":com_squareup_okhttp3_okhttp",
193 | ":com_squareup_okio_okio",
194 | ],
195 | )
196 |
197 | native.java_library(
198 | name = "org_checkerframework_checker_qual",
199 | visibility = ["//visibility:public"],
200 | exports = ["@org_checkerframework_checker_qual//jar"],
201 | )
202 |
203 | native.java_library(
204 | name = "com_squareup_okhttp3_okhttp",
205 | visibility = ["//visibility:public"],
206 | exports = ["@com_squareup_okhttp3_okhttp//jar"],
207 | runtime_deps = [
208 | ":com_squareup_okio_okio",
209 | ],
210 | )
211 |
212 | native.java_library(
213 | name = "com_google_errorprone_error_prone_annotations",
214 | visibility = ["//visibility:public"],
215 | exports = ["@com_google_errorprone_error_prone_annotations//jar"],
216 | )
217 |
218 | native.java_library(
219 | name = "org_apache_commons_commons_lang3",
220 | visibility = ["//visibility:public"],
221 | exports = ["@org_apache_commons_commons_lang3//jar"],
222 | )
223 |
224 | native.java_library(
225 | name = "com_squareup_okio_okio",
226 | visibility = ["//visibility:public"],
227 | exports = ["@com_squareup_okio_okio//jar"],
228 | )
229 |
230 | native.java_library(
231 | name = "com_google_guava_guava",
232 | visibility = ["//visibility:public"],
233 | exports = ["@com_google_guava_guava//jar"],
234 | runtime_deps = [
235 | ":com_google_code_findbugs_jsr305",
236 | ":com_google_errorprone_error_prone_annotations",
237 | ":com_google_j2objc_j2objc_annotations",
238 | ":org_checkerframework_checker_qual",
239 | ":org_codehaus_mojo_animal_sniffer_annotations",
240 | ],
241 | )
242 |
243 | native.java_library(
244 | name = "org_bouncycastle_bcprov_jdk15on",
245 | visibility = ["//visibility:public"],
246 | exports = ["@org_bouncycastle_bcprov_jdk15on//jar"],
247 | )
248 |
249 | native.java_library(
250 | name = "com_beust_jcommander",
251 | visibility = ["//visibility:public"],
252 | exports = ["@com_beust_jcommander//jar"],
253 | )
254 |
255 | native.java_library(
256 | name = "org_slf4j_slf4j_simple",
257 | visibility = ["//visibility:public"],
258 | exports = ["@org_slf4j_slf4j_simple//jar"],
259 | runtime_deps = [
260 | ":org_slf4j_slf4j_api",
261 | ],
262 | )
263 |
264 | native.java_library(
265 | name = "junit_junit",
266 | visibility = ["//visibility:public"],
267 | exports = ["@junit_junit//jar"],
268 | runtime_deps = [
269 | ":org_hamcrest_hamcrest_core",
270 | ],
271 | )
272 |
273 | native.java_library(
274 | name = "com_google_j2objc_j2objc_annotations",
275 | visibility = ["//visibility:public"],
276 | exports = ["@com_google_j2objc_j2objc_annotations//jar"],
277 | )
278 |
279 | native.java_library(
280 | name = "org_iota_jota",
281 | visibility = ["//visibility:public"],
282 | exports = ["@org_iota_jota//jar"],
283 | )
284 |
--------------------------------------------------------------------------------