#wrapper.p-5
  h1.text-2xl.font-bold.text-center Mining Simulator
  
  #root
View Compiled
* {
  font-family: proxima-nova, sans-serif;
}

#wrapper {
  max-width: 350px;
  margin: auto;
}

p {
  color: #4D4D4D;
}
View Compiled
async function sha256(message) {
  // encode as UTF-8
  const msgBuffer = new TextEncoder("utf-8").encode(message)

  // hash the message
  const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer)

  // convert ArrayBuffer to Array
  const hashArray = Array.from(new Uint8Array(hashBuffer))

  // convert bytes to hex string
  const hashHex = hashArray
    .map((b) => ("00" + b.toString(16)).slice(-2))
    .join("")
  return hashHex
}

const { useState, unstable_useTransition: useTransition, useCallback, useRef, useEffect } = React

const useStateAndRef = (defaultValue) => {
  const [state, setState] = useState(defaultValue)
  const ref = useRef(defaultValue)
  useEffect(() => {
    ref.current = state
  }, [state])
  
  return [state, setState, ref]
}

const timeFormatter = new Intl.NumberFormat('en-US', { maximumSignificantDigits: 3 })
const hashRateFormatter = new Intl.NumberFormat('en-US', { maximumSignificantDigits: 4 })

const addToAverage = (currentAverage, newValue, nthValue) => {
  return (currentAverage * ((nthValue - 1) / nthValue)) + (newValue * (1 / nthValue))
}

const Component = () => {
  const [input, setInput] = useState("")
  const [displayNonce, setDisplayNonce] = useState(0)
  const [difficulty, setDifficulty] = useState(1)
  const nonce = useRef(0)
  const lastHash = useRef("")
  const [running, setRunning, runningRef] = useStateAndRef(false)
  const [output, setOutput] = useState("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
  const [smallestHash, setSmallestHash] = useState("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
  const [startTransition, isPending] = useTransition({ timeoutMs: 5000 })
  useEffect(() => {
    if (running) {
      let targetHex = ""
      for (let i = 0; i < 64; i++) {
        targetHex += i < difficulty ? "0" : "f"
      }
      const target = parseInt(targetHex, 16)
      const searchForNonce = () => {
        sha256(input + nonce.current).then(hash => {
          if (parseInt(hash, 16) <= target) {
            setRunning(false)
            setOutput(hash)
            setDisplayNonce(nonce.current)
          } else {
            nonce.current++
            lastHash.current = hash
            if (runningRef.current) searchForNonce()
          }
        })
      }
      const renderProgress = () => {
        setDisplayNonce(nonce.current)
        setOutput(lastHash.current)
      }
      const renderTimer = setInterval(renderProgress, 32)
      searchForNonce()
      return () => clearInterval(renderTimer)
    }
  }, [running, input])
  const onChange = useCallback(e => { setInput(e.target.value) })
  useEffect(() => {
    if (!running) {
      sha256(input + displayNonce).then(result => setOutput(result))
    }
  }, [input, displayNonce, running])
  
  let renderedDifficulty = ""
  for (let i = 0; i < difficulty; i++) {
    renderedDifficulty += "0"
  }
  
  return (
    <>
      <label className="font-semibold">Pretend Block Data</label>
      <textarea className="border-gray-300 border-solid border bg-gray-200 rounded w-full p-2 mb-4 font-mono placeholder-gray-500" value={input} onChange={onChange} placeholder="Type anything in here" disabled={running} />
      <div className="flex flex-col items-center mb-4">
        <label className="font-semibold">Difficulty</label>
        <code className="border-gray-300 border-solid border bg-gray-200 rounded p-1 font-mono text-sm">{renderedDifficulty}</code>
        <input type="range" min={1} max={6} value={difficulty} onChange={e => setDifficulty(e.target.value)} disabled={running}/>
      </div>
      
      <label className="font-semibold">Nonce</label>
      <div className="border-gray-300 border-solid border bg-gray-200 rounded w-full p-2 font-mono text-sm break-all mb-4">
        {displayNonce} 
      </div>
      <span className="flex rounded-md shadow-sm mb-4">
        <button type="button" className="flex flex-1 items-center justify-center px-3 py-2 border border-gray-300 text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:text-gray-800 active:bg-gray-50 transition ease-in-out duration-150" onClick={() => setRunning(r => !r)}>
          {running ? "Running..." : "Start"}
        </button>
      </span>
      <div className="flex justify-between">
        <label className="font-semibold">SHA-256 Output</label>
      </div>
      <div className="border-gray-300 border-solid border bg-gray-200 rounded w-full p-2 font-mono text-sm break-all">
        {output} 
      </div>
    </>
  )
}

ReactDOM.unstable_createRoot(document.getElementById("root")).render(<Component />)
View Compiled

External CSS

  1. https://cdn.jsdelivr.net/npm/@tailwindcss/ui@latest/dist/tailwind-ui.min.css
  2. https://use.typekit.net/tjo5maf.css

External JavaScript

  1. https://cdn.jsdelivr.net/npm/react@experimental/umd/react.development.min.js
  2. https://cdn.jsdelivr.net/npm/react-dom@experimental/umd/react-dom.development.min.js