/ 2.html
2.html
  1  <!DOCTYPE html>
  2  <html lang="en">
  3  <head>
  4      <meta charset="UTF-8">
  5      <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6      <title>Ethereum Transaction Lifecycle Visualizer</title>
  7      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
  8      <style>
  9          body, html { 
 10              margin: 0; 
 11              padding: 0; 
 12              width: 100%; 
 13              height: 100%; 
 14              overflow: hidden;
 15              font-family: Arial, sans-serif;
 16          }
 17          #visualization { 
 18              width: 100%; 
 19              height: 100%; 
 20          }
 21          #controls { 
 22              position: fixed;
 23              bottom: 20px;
 24              right: 20px;
 25              width: 150px; 
 26              height: 150px; 
 27          }
 28          .control-button { 
 29              position: absolute; 
 30              width: 60px; 
 31              height: 30px; 
 32              background-color: rgba(255, 255, 255, 0.7);
 33              border: 1px solid #999;
 34              border-radius: 5px;
 35          }
 36          #upBtn { top: 0; left: 45px; }
 37          #leftBtn { top: 60px; left: 0; }
 38          #rightBtn { top: 60px; right: 0; }
 39          #downBtn { bottom: 0; left: 45px; }
 40          #metadata, #addNodeForm {
 41              position: fixed;
 42              background-color: rgba(255, 255, 255, 0.9);
 43              padding: 10px;
 44              border-radius: 5px;
 45              max-width: 300px;
 46              max-height: 80%;
 47              overflow-y: auto;
 48          }
 49          #metadata {
 50              bottom: 20px;
 51              left: 20px;
 52          }
 53          #addNodeForm {
 54              top: 20px;
 55              left: 20px;
 56              display: none;  /* Initially hidden */
 57          }
 58          #metadata input, #metadata textarea, #addNodeForm input, #addNodeForm select {
 59              width: 100%;
 60              margin-bottom: 10px;
 61          }
 62          #metadata button, #addNodeForm button {
 63              margin-right: 10px;
 64          }
 65          #editToggle {
 66              position: fixed;
 67              top: 20px;
 68              right: 20px;
 69          }
 70      </style>
 71  </head>
 72  <body onload="onLoadPopulateNodesFromURl()">
 73      <div id="Title", style="padding-left: 10px; padding-right: 10px;">
 74          <h1>Preconfirmations</h1>
 75      </div>
 76      <div id="Subtitle", style="padding-left: 10px; padding-right: 10px;">
 77          <h2>Fast, credible promises of inclusion</h2>
 78      </div>
 79      <div id="Content", style="padding-left: 10px; padding-right: 10px;">
 80  <p>Preconfirmations insert a new stage to this progression of confidence, between submission and inclusion.</p>
 81  
 82  <p>It does this by enabling parties involved in the transaction lifecyle to provide a stake-backed promise that the transaction will be included within a stupulated time, or even included with the surety of a specific
 83  post-inclusion state (the ENS is succesfully registered, or the Uniswap transaction is placed at the top of the block, for example). We will get to who these parties are, both on the supply/submission
 84  and demand/preconfirmation side in due course. The parties providing the preconfirmations will henceforth be called <strong>preconfers</strong>.</p>
 85  
 86  <p>So from a user's perspective, a transaction now goes through the following stages: Submission > <strong>Preconfirmation</strong> > Inclusion > Finalization </p>
 87  
 88  <p>These preconfirmations can be provided at a much smaller interval than the 12 seconds per slot, depending on the specific design of the preconfirmation supply chain.</p>
 89  <span style="float: right;"><a href="1.html"> &lt;&lt;prev&lt;&lt; </a>&nbsp;<a href="3.html"> >>Next>> </a></span>
 90      </div>
 91      <div id="visualization"></div>
 92      <div id="controls">
 93          <button id="upBtn" class="control-button">Up</button>
 94          <button id="leftBtn" class="control-button">Previous</button>
 95          <button id="rightBtn" class="control-button">Next</button>
 96          <button id="downBtn" class="control-button">Down</button>
 97      </div>
 98      <div id="metadata"></div>
 99      <div id="addNodeForm">
100          <h3>Add New Node</h3>
101          <input type="text" id="newNodeName" placeholder="Node Name">
102          <select id="newNodeDirection">
103              <option value="next">Next</option>
104              <option value="up">Up</option>
105              <option value="down">Down</option>
106          </select>
107          <button onclick="addNewNode()">Add Node</button>
108      </div>
109      <button id="editToggle" onclick="toggleEditMode()">Edit Mode</button>
110  
111      <script>
112          let nodeIds = ["User"];
113          let connectionMatrix = {
114              "User": {}
115          };
116          let nodeMetadata = {
117              "User": {
118                  "content": "Initiates and signs the transaction",
119                  "url": "https://ethereum.org/en/developers/docs/transactions/"
120              }
121          };
122  
123          const directionToGridDelta = {
124              "next": [1, 0],
125              "previous": [-1, 0],
126              "up": [0, -1],
127              "down": [0, 1]
128          };
129  
130          let isEditMode = false;
131  
132          function assignGridPositions(startNode) {
133              const gridPositions = {};
134              const queue = [[startNode, 0, 0]];
135              const visited = new Set();
136  
137              while (queue.length > 0) {
138                  const [node, x, y] = queue.shift();
139                  if (visited.has(node)) continue;
140                  visited.add(node);
141                  gridPositions[node] = [x, y];
142  
143                  for (let neighbor in connectionMatrix[node]) {
144                      const connection = connectionMatrix[node][neighbor];
145                      if (connection) {
146                          const [dx, dy] = directionToGridDelta[connection[0]];
147                          queue.push([neighbor, x + dx, y + dy]);
148                      }
149                  }
150              }
151  
152              return gridPositions;
153          }
154  
155          function createVisualization() {
156              const width = window.innerWidth;
157              const height = window.innerHeight;
158              const nodeRadius = Math.min(width, height) * 0.05;
159              const transactionRadius = nodeRadius * 0.3;
160              const gridSize = Math.min(width, height) * 0.2;
161  
162              d3.select("#visualization").selectAll("*").remove();
163  
164              const gridPositions = assignGridPositions(nodeIds[0]);
165  
166              let nodes = nodeIds.map(id => ({
167                  id: id,
168                  x: (gridPositions[id][0] + 2) * gridSize,
169                  y: (gridPositions[id][1] + 2) * gridSize
170              }));
171  
172              const links = [];
173              for (let source in connectionMatrix) {
174                  for (let target in connectionMatrix[source]) {
175                      if (connectionMatrix[source][target]) {
176                          links.push({
177                              source: nodes.find(n => n.id === source),
178                              target: nodes.find(n => n.id === target),
179                              type: connectionMatrix[source][target][0]
180                          });
181                      }
182                  }
183              }
184  
185              const svg = d3.select("#visualization")
186                  .append("svg")
187                  .attr("width", width)
188                  .attr("height", height);
189  
190              const link = svg.append("g")
191                  .selectAll("line")
192                  .data(links)
193                  .join("line")
194                  .attr("stroke", "#999")
195                  .attr("stroke-opacity", 0.6)
196                  .attr("x1", d => d.source.x)
197                  .attr("y1", d => d.source.y)
198                  .attr("x2", d => d.target.x)
199                  .attr("y2", d => d.target.y);
200  
201              const node = svg.append("g")
202                  .selectAll("circle")
203                  .data(nodes)
204                  .join("circle")
205                  .attr("r", nodeRadius)
206                  .attr("fill", "lightblue")
207                  .attr("cx", d => d.x)
208                  .attr("cy", d => d.y);
209  
210              const label = svg.append("g")
211                  .selectAll("text")
212                  .data(nodes)
213                  .join("text")
214                  .text(d => d.id)
215                  .attr("font-size", nodeRadius * 0.3)
216                  .attr("text-anchor", "middle")
217                  .attr("dominant-baseline", "central")
218                  .attr("x", d => d.x)
219                  .attr("y", d => d.y);
220  
221              const transaction = svg.append("circle")
222                  .attr("r", transactionRadius)
223                  .attr("fill", "red");
224  
225              return { nodes, transaction };
226          }
227  
228          let { nodes, transaction } = createVisualization();
229          let currentNode = nodes[0];
230          transaction.attr("cx", currentNode.x).attr("cy", currentNode.y);
231  
232          const directionMapping = {
233              "up": "up",
234              "down": "down",
235              "next": "right",
236              "previous": "left"
237          };
238  
239          function updateMetadataDisplay(nodeId) {
240              const metadata = nodeMetadata[nodeId];
241              let html = `<h3><a href="${metadata.url}" target="_blank">${nodeId}</a></h3>`;
242              if (isEditMode) {
243                  html += `
244                      <form id="metadataForm">
245                          <input type="text" id="nodeNameInput" value="${nodeId}">
246                          <textarea id="content" name="content" placeholder="Content">${metadata.content}</textarea>
247                          <input type="url" id="url" name="url" value="${metadata.url}" placeholder="URL">
248                          <button type="button" onclick="saveMetadata('${nodeId}')">Save</button>
249                          <button type="button" onclick="deleteNode('${nodeId}')">Delete</button>
250                          <button type="button" onclick="uploadNodeData()">Upload</button>
251                          <button type="button" onclick="downloadNodeData()">Download</button>
252                      </form>`;
253              } else {
254                  html += `<p>${metadata.content}</p>`;
255              }
256              document.getElementById("metadata").innerHTML = html;
257          }
258  
259          function saveMetadata(oldNodeId) {
260              const newNodeId = document.getElementById("nodeNameInput").value;
261              const content = document.getElementById("content").value;
262              const url = document.getElementById("url").value;
263              
264              const newMetadata = {
265                  content: content,
266                  url: url
267              };
268  
269              if (oldNodeId !== newNodeId) {
270                  nodeMetadata[newNodeId] = newMetadata;
271                  delete nodeMetadata[oldNodeId];
272                  nodeIds = nodeIds.map(id => id === oldNodeId ? newNodeId : id);
273                  
274                  // Update connectionMatrix
275                  connectionMatrix[newNodeId] = connectionMatrix[oldNodeId];
276                  delete connectionMatrix[oldNodeId];
277                  for (let node in connectionMatrix) {
278                      if (connectionMatrix[node][oldNodeId]) {
279                          connectionMatrix[node][newNodeId] = connectionMatrix[node][oldNodeId];
280                          delete connectionMatrix[node][oldNodeId];
281                      }
282                  }
283  
284                  currentNode = { id: newNodeId, x: currentNode.x, y: currentNode.y };
285                  //Update the nodes object
286                  nodes = nodes.map(n => n.id === oldNodeId ? currentNode : n);
287                  console.log(nodes);
288              } else {
289                  nodeMetadata[newNodeId] = newMetadata;
290              }
291  
292              ({ nodes, transaction } = createVisualization());
293              transaction.attr("cx", currentNode.x).attr("cy", currentNode.y);
294              updateButtons();
295              updateMetadataDisplay(newNodeId);
296          }
297  
298          function resetMetadata(nodeId) {
299              nodeMetadata[nodeId] = {
300                  "content": "",
301                  "url": ""
302              };
303              updateMetadataDisplay(nodeId);
304          }
305  
306          function moveTransaction(direction) {
307              for (let targetId in connectionMatrix[currentNode.id]) {
308                  const connection = connectionMatrix[currentNode.id][targetId];
309                  if (connection && connection[0] === direction) {
310                      const targetNode = nodes.find(n => n.id === targetId);
311                      if (targetNode) {
312                          currentNode = targetNode;
313                          transaction.transition()
314                              .duration(500)
315                              .attr("cx", currentNode.x)
316                              .attr("cy", currentNode.y);
317                          updateButtons();
318                          updateMetadataDisplay(currentNode.id);
319                          break;
320                      }
321                  }
322              }
323          }
324  
325          function updateButtons() {
326              for (let direction in directionMapping) {
327                  const buttonId = `#${directionMapping[direction]}Btn`;
328                  let isEnabled = false;
329                  for (let targetId in connectionMatrix[currentNode.id]) {
330                      const connection = connectionMatrix[currentNode.id][targetId];
331                      if (connection && connection[0] === direction) {
332                          isEnabled = true;
333                          break;
334                      }
335                  }
336                  d3.select(buttonId).property("disabled", !isEnabled);
337              }
338          }
339  
340          function toggleEditMode() {
341              isEditMode = !isEditMode;
342              updateMetadataDisplay(currentNode.id);
343              document.getElementById("editToggle").textContent = isEditMode ? "View Mode" : "Edit Mode";
344              document.getElementById("addNodeForm").style.display = isEditMode ? "block" : "none";
345          }
346  
347          function addNewNode() {
348              const newNodeName = document.getElementById("newNodeName").value;
349              const direction = document.getElementById("newNodeDirection").value;
350              
351              if (!newNodeName || nodeIds.includes(newNodeName)) {
352                  alert("Please enter a unique node name.");
353                  return;
354              }
355  
356              nodeIds.push(newNodeName);
357              connectionMatrix[newNodeName] = {};
358              nodeMetadata[newNodeName] = {
359                  "content": "",
360                  "url": ""
361              };
362  
363              connectionMatrix[currentNode.id][newNodeName] = [direction, directionToGridDelta[direction]];
364              const oppositeDirection = direction === "next" ? "previous" : (direction === "up" ? "down" : "up");
365              connectionMatrix[newNodeName][currentNode.id] = [oppositeDirection, directionToGridDelta[oppositeDirection]];
366  
367              ({ nodes, transaction } = createVisualization());
368              
369              // Move the transaction to the new node
370              currentNode = nodes.find(n => n.id === newNodeName);
371              transaction.transition()
372                  .duration(500)
373                  .attr("cx", currentNode.x)
374                  .attr("cy", currentNode.y);
375  
376              updateButtons();
377              updateMetadataDisplay(currentNode.id);
378  
379              // Clear the input field
380              document.getElementById("newNodeName").value = "";
381          }
382  
383          d3.select("#upBtn").on("click", () => moveTransaction("up"));
384          d3.select("#downBtn").on("click", () => moveTransaction("down"));
385          d3.select("#leftBtn").on("click", () => moveTransaction("previous"));
386          d3.select("#rightBtn").on("click", () => moveTransaction("next"));
387  
388          updateButtons();
389          updateMetadataDisplay(currentNode.id);
390  
391          window.addEventListener('resize', () => {
392              ({ nodes, transaction } = createVisualization());
393              currentNode = nodes.find(n => n.id === currentNode.id);
394              transaction.attr("cx", currentNode.x).attr("cy", currentNode.y);
395              updateButtons();
396              updateMetadataDisplay(currentNode.id);
397          });
398  
399          function deleteNode(nodeId) {
400              if (nodeId === "User") {
401                  alert("Cannot delete the initial node.");
402                  return;
403              }
404  
405              delete nodeMetadata[nodeId];
406              delete connectionMatrix[nodeId];
407              nodeIds = nodeIds.filter(id => id !== nodeId);
408              for (let node in connectionMatrix) {
409                  delete connectionMatrix[node][nodeId];
410              }
411  
412              ({ nodes, transaction } = createVisualization());
413              currentNode = nodes[0];
414              transaction.attr("cx", currentNode.x).attr("cy", currentNode.y);
415              updateButtons();
416              updateMetadataDisplay(currentNode.id);
417          }
418  
419          function downloadNodeData() {
420              const data = {
421                  nodeIds: nodeIds,
422                  connectionMatrix: connectionMatrix,
423                  nodeMetadata: nodeMetadata
424              };
425              const blob = new Blob([JSON.stringify(data)], { type: "application/json" });
426              const url = URL.createObjectURL(blob);
427              const a = document.createElement("a");
428              a.href = url;
429              a.download = "preconfirmations.json";
430              a.click();
431          }
432  
433          function uploadNodeData() {
434              const input = document.createElement("input");
435              input.type = "file";
436              input.accept = ".json";
437              input.onchange = e => {
438                  const file = e.target.files[0];
439                  const reader = new FileReader();
440                  reader.onload = e => {
441                      const data = JSON.parse(e.target.result);
442                      nodeIds = data.nodeIds;
443                      connectionMatrix = data.connectionMatrix;
444                      nodeMetadata = data.nodeMetadata;
445                      ({ nodes, transaction } = createVisualization());
446                      currentNode = nodes[0];
447                      transaction.attr("cx", currentNode.x).attr("cy", currentNode.y);
448                      updateButtons();
449                      updateMetadataDisplay(currentNode.id);
450                  };
451                  reader.readAsText(file);
452              };
453              input.click();
454          }
455  
456          function downloadNodeData() {
457              const data = {
458                  nodeIds: nodeIds,
459                  connectionMatrix: connectionMatrix,
460                  nodeMetadata: nodeMetadata
461              };
462              const blob = new Blob([JSON.stringify(data)], { type: "application/json" });
463              const url = URL.createObjectURL(blob);
464              const a = document.createElement("a");
465              a.href = url;
466              a.download = "preconfirmations.json";
467              a.click();
468          }
469  
470          function onLoadPopulateNodesFromURl() {
471              //Fetch json file from relative URL, check if it exists, if so populate the nodes
472              fetch('2.json')
473                  .then(response => response.json())
474                  .then(data => {
475                      nodeIds = data.nodeIds;
476                      connectionMatrix = data.connectionMatrix;
477                      nodeMetadata = data.nodeMetadata;
478                      ({ nodes, transaction } = createVisualization());
479                      currentNode = nodes[0];
480                      transaction.attr("cx", currentNode.x).attr("cy", currentNode.y);
481                      updateButtons();
482                      updateMetadataDisplay(currentNode.id);
483                  })
484                  .catch((error) => {
485                      console.error('Error:', error);
486                  });
487          }
488  
489      </script>
490  </body>
491  </html>