Pages:
Author

Topic: Antminer S9: Unconventional Application for Provably Fair Dice Games - page 2. (Read 327 times)

member
Activity: 196
Merit: 60
...

1. You chose the wrong board to post this. Although it is related to gambling, it is better suited for Development & Technical Discussion. You will find many members with ample knowledge there who can help you a lot more. You can move this topic via move topic button located at the bottom left corner of this page.

2. You are a newbie so you cannot post images directly. You must be at least a Jr. member in order to post images OR you can buy Copper membership to enable that function. You can still post the links to the images.
legendary
Activity: 2688
Merit: 3789
If you implement hash mining via ASIC and try to integrate this mechanics into some kind of online casino, then many questions immediately arise. Firstly, how to balance the chances of different players to win? After all, one player can use one device, and another can use a whole farm. Send an inspector to check the number of devices used? Quite a controversial decision. Again, you need to take into account that the value of the prize is higher than the cost of the funds spent. This is not so easy to do. Most likely, some standard solutions are needed. I would even recommend that we think about mining simulation. It is easier to control and standardize.
brand new
Activity: 0
Merit: 0
Hey thanks for the reply.
I don't know how to post images here they keep failing but I'd gladly Show you how practical they are for this use case. The only reason I chose the s9 is the SDK was readily available to tinker with  and I had some hanging around. to use.
legendary
Activity: 2422
Merit: 1451
Leading Crypto Sports Betting & Casino Platform
This is very unorthodox because I take it the randomness from an S9 isn't ideal given it's propensity to search for certain patterns. So it's likely there won't be much of a practical application in this as much as it might be a learning experience to set it up though.

So while this isn't exactly useful, it's not like we haven't seen other unconventional applications. Cloud flare is said to have been using ksvs lamps for randomness on SSL certificate assigning. It's very impractical and unorthodox but they swore by that practice to introduce randomness. The thing with the S9 though is that it's also a computer so it loses any sort of charm a non computer object like a lava lamp would bring. For a crypto crowd though, it's ok I guess.
legendary
Activity: 2338
Merit: 1775
Catalog Websites
This is a very interesting concept! Many people forget that mining is essentially a game of chance, this human activity can be considered not only as the extraction of virtual minerals, but as a lottery game.

In the lottery, a player can increase his chances of winning by buying additional tickets. A miner increases his chances of winning by increasing his computing power (that is, buying more ASICs or buying those with more power). In any case, miners compete with each other, that is, it is a zero-sum game. Someone wins, and someone loses.

The very idea of ​​using ASICs not for their intended purpose, in my opinion, is very original and interesting.
brand new
Activity: 0
Merit: 0
You would be right to assume the entire key space is impossible and massive.
but if you take some random seeds:

You take a small sample and, based on this, assert that the entire range obeys a certain pattern. There are patterns in the number series that are really rare, such as 7777777 or dfdfdf, etc. But they make up tiny fractions of the entire range, so it will not be possible to significantly reduce the range. The larger the sample, the less your bias will be. And the whole range is needed for the game. Another option is that there may be some flaw in the RNG, but that's a completely different question.

The sample was for demonstrational purposes only.

as per the PRNG
Code:
2025-01-27 08:32:05,153 - INFO - Observing 624 outcomes for PRNG state recovery...
2025-01-27 08:32:07,633 - INFO - Outcome observation complete.
2025-01-27 08:32:07,633 - INFO - Attempting to recover PRNG state...
2025-01-27 08:32:07,644 - INFO - PRNG state recovery successful.
2025-01-27 08:32:07,644 - INFO - Testing prediction accuracy with 1 trials...
2025-01-27 08:32:07,649 - INFO - Prediction accuracy: 1.0000
2025-01-27 08:32:07,649 - INFO - Testing prediction accuracy with 1 trials...
2025-01-27 08:32:07,654 - INFO - Prediction accuracy: 1.0000
2025-01-27 08:32:07,654 - INFO - Initiating exploitation using PRNG prediction...
2025-01-27 08:32:07,659 - INFO - PRNG Exploit: Predicted Low (1.00), Actual: 1.00, Profit: 0.00
2025-01-27 08:32:07,659 - WARNING - PRNG Exploitation complete. Total Profit: 0.00, Number of Bets: 1, Win Rate: 1.00

proof of concept:

Code:
import hmac
import hashlib
import secrets
import logging
import time
import random
from collections import deque
import pandas as pd
from autogluon.tabular import TabularPredictor
import statistics
from scipy.stats import norm

from functions import untemper, rewindState, seedArrayFromState, seedArrayToInt

logging.basicConfig(level=logging.INFO, format='{asctime} - {levelname} - {message}', style='{')

class VulnerableStakeSimulator:
    """
    A more robust and realistic simulation, now explicitly using Python's random.Random.
    """
    def __init__(self, bias=0.005, predictable_next_seed=False, timing_variance=0.005, network_latency_variance=0.002):
        self.internal_rng = random.Random()
        self.current_server_seed = secrets.token_hex(32)
        self.next_server_seed = secrets.token_hex(32)
        self.revealed_server_seeds = {}
        self.bias = bias  # A very subtle bias
        self.predictable_next_seed = predictable_next_seed
        self.timing_variance = timing_variance
        self.network_latency_variance = network_latency_variance

    def get_next_server_seed_hash(self):
        return hashlib.sha256(self.next_server_seed.encode()).hexdigest()

    def roll_dice(self, client_seed: str, nonce: int) -> tuple[float, str]:
        processing_delay = random.uniform(0, self.timing_variance)
        network_delay = random.uniform(0, self.network_latency_variance)
        time.sleep(processing_delay + network_delay)

        server_seed = self.current_server_seed
        hmac_message = f"{server_seed}:{client_seed}:{nonce}"
        hmac_hash = hmac.new(server_seed.encode(), hmac_message.encode(), hashlib.sha256).hexdigest()

        lucky_result = self.internal_rng.random()

        outcome = min(lucky_result * 100 + self.bias, 100)
        self.revealed_server_seeds[nonce] = server_seed
        return outcome, server_seed

    def reveal_next_server_seed(self):
        if self.predictable_next_seed:
            return self.next_server_seed
        else:
            raise Exception("Next server seed is not designed to be predictable in this simulation.")

    def rotate_seeds(self):
        self.current_server_seed = self.next_server_seed
        if self.predictable_next_seed:
            self.next_server_seed = hashlib.sha256(self.current_server_seed.encode()).hexdigest()[:32]
        else:
            self.next_server_seed = secrets.token_hex(32)

class TrulyDemonstratingInnovativePoCAdvanced:
    """
    A Proof-of-Concept demonstrating advanced exploitation with robust testing before wagering.
    """
    def __init__(self, simulator: VulnerableStakeSimulator):
        self.simulator = simulator
        self.observed_outcomes = deque(maxlen=624)
        self.recovered_rng_state = None
        self.prediction_accuracy_threshold = 0.50  # Require 95% accuracy on tests

    def observe_outcomes(self, num_observations=624):
        """Observe a sequence of outcomes for PRNG state recovery."""
        logging.info(f"Observing {num_observations} outcomes for PRNG state recovery...")
        for i in range(num_observations):
            outcome, _ = self.simulator.roll_dice("observer", i + 1)
            self.observed_outcomes.append(outcome)
        logging.info("Outcome observation complete.")

    def recover_prng_state(self):
        """Attempt to recover the internal state of the simulator's PRNG."""
        if len(self.observed_outcomes) < 624:
            logging.warning("Not enough observed outcomes to recover PRNG state.")
            return

        logging.info("Attempting to recover PRNG state...")
        try:
            scaled_outcomes = [outcome / 100 for outcome in self.observed_outcomes]
            untempered_outputs = [untemper(int((x * (2**32)) % (2**32))) for x in scaled_outcomes]
            self.recovered_rng_state = (3, tuple(untempered_outputs + [624]), None)
            logging.info("PRNG state recovery successful.")
        except Exception as e:
            logging.error(f"PRNG state recovery failed: {e}")
            self.recovered_rng_state = None

    def test_prediction_accuracy(self, num_tests=1):
        """Test the accuracy of predictions based on the recovered PRNG state."""
        if not self.recovered_rng_state:
            logging.warning("No recovered PRNG state to test.")
            return False

        logging.info(f"Testing prediction accuracy with {num_tests} trials...")
        predicted_rng = random.Random()
        #predict above or below 50 only

        predicted_rng.setstate(self.recovered_rng_state)

        correct_predictions = 0

        for _ in range(num_tests):
            predicted_value = predicted_rng.random()
            if predicted_value < 50.5:
                predicted_value = 1

            else:
                predicted_value = 0
            actual_outcome, _ = self.simulator.roll_dice("tester", len(self.observed_outcomes) + _ + 1)
            actual_value = actual_outcome / 100.0
            if actual_value < 50.5:
                actual_value = 1
            else:
                actual_value = 0


            # Use a tolerance for floating-point comparison
            if predicted_value == actual_value:
                correct_predictions += 1
            else:
                correct_predictions += 0


        accuracy = correct_predictions / num_tests
        logging.info(f"Prediction accuracy: {accuracy:.4f}")
        return accuracy >= self.prediction_accuracy_threshold

    def exploit_with_prng_prediction(self, num_exploitative_rolls=624):
        """Exploit the simulator by predicting future outcomes, with prior testing."""
        if not self.recovered_rng_state:
            logging.warning("No recovered PRNG state available for prediction.")
            return

        if not self.test_prediction_accuracy():
            logging.warning("Prediction accuracy test failed. Aborting wagering.")
            return

        logging.info("Initiating exploitation using PRNG prediction...")
        predicted_rng = random.Random()
        predicted_rng.setstate(self.recovered_rng_state)
        bet_amount = 1.0
        total_profit = 0
        num_bets = 0
        num_wins = 0

        for i in range(num_exploitative_rolls):
            predicted_outcome = predicted_rng.random() * 100
            actual_outcome, _ = self.simulator.roll_dice("exploiter_prng", len(self.observed_outcomes) + i + 1)
            predicted_outcome = 1 if predicted_outcome < 50.5 else 0
            actual_outcome = 1 if actual_outcome < 50.5 else 0


            bet_placed = False
            if predicted_outcome == actual_outcome:
                profit = bet_amount * (actual_outcome < 50) - bet_amount
                total_profit += profit
                num_bets += 1
                num_wins += (actual_outcome < 50)
                logging.info(f"PRNG Exploit: Predicted Low ({predicted_outcome:.2f}), Actual: {actual_outcome:.2f}, Profit: {profit:.2f}")
                bet_placed = True
            else:
               print("failed prediction")

        if num_bets > 0:
            win_rate = num_wins / num_bets if num_bets > 0 else 0
            logging.warning(f"PRNG Exploitation complete. Total Profit: {total_profit:.2f}, Number of Bets: {num_bets}, Win Rate: {win_rate:.2f}")
        else:
            logging.warning("PRNG Exploitation complete. No exploitable opportunities found.")

if __name__ == "__main__":
    print("\n--- Demonstrating Advanced Exploitation with PRNG State Recovery and Testing ---")
    vulnerable_simulator_prng = VulnerableStakeSimulator(bias=0.0)
    poc_prng = TrulyDemonstratingInnovativePoCAdvanced(vulnerable_simulator_prng)

    poc_prng.observe_outcomes()
    poc_prng.recover_prng_state()

    if poc_prng.recovered_rng_state:
        if poc_prng.test_prediction_accuracy():
            poc_prng.exploit_with_prng_prediction(num_exploitative_rolls=1)
        else:
            logging.warning("Insufficient prediction accuracy. Not proceeding with wagering.")
    else:
        logging.warning("PRNG state recovery failed. Cannot proceed with testing or wagering.")

hero member
Activity: 1736
Merit: 857
You would be right to assume the entire key space is impossible and massive.
but if you take some random seeds:

You take a small sample and, based on this, assert that the entire range obeys a certain pattern. There are patterns in the number series that are really rare, such as 7777777 or dfdfdf, etc. But they make up tiny fractions of the entire range, so it will not be possible to significantly reduce the range. The larger the sample, the less your bias will be. And the whole range is needed for the game. Another option is that there may be some flaw in the RNG, but that's a completely different question.
legendary
Activity: 2688
Merit: 3789
The idea of ​​using ASIC for game mechanics, in particular Antminer S9 or some other, is original and interesting. You can try to implement this idea in the form of a kind of casino game. It is strange that no one has thought of this before. However, we should also consider the negative aspects of using such devices. Firstly, if you create a casino game for many players on this basis, then the question arises of where to get similar devices for many players. Should players have their own devices? It is a bit strange to come to the casino with your own device. What if different players have devices of different power? Therefore, someone will have an unfair advantage. So, before the start of the competition, the devices need to be checked somehow. There is an option to rent out the devices or use the Antminer S9 simulator that is the same for everyone. In this case, it will be pseudo-mining. It is also not entirely clear whether this will be a game of one person against the casino, as in the case of roulette, or it will be a competition of several miners for 1 prize set by the casino.
brand new
Activity: 0
Merit: 0
What are your sources or calcultations about the estimated amount of time for cracking un hash SHA-256? Especially "Seconds/Minutes" for an Antminer S9? If it were true, SHA-256 wouldn't be secured anymore and no one would use it. So I think if the sever seed is generated randomly and is a long string, you won't be able to find it with such method. It could only work if there are some rules, patterns or narrow bounds for generating the sever seed.

You would be right to assume the entire key space is impossible and massive.
but if you take some random seeds:

Code:
hashes = [
    "3d9d7ac2a7bf7a3755bede703a63a0fa17d0e206a5752555717432267fd9709e",
    "04e3b4023148040c4d000ff3493107317dc2eef513fe7bfbfbe706890c376846",
    "4e173939c30fccda068c5f959f7648c1d6a8625ba9edc6324dbdf74366d5f158",
    "2a108a45ed6ea108dfb8041e6f26df56d4c4b6dbf95184472212d50f8e951914",
    "4d4410cda3b52677d0f76087ab222d9fb2c3de8265b32660d6c9be0404938222",
    "fe8ff24859844caa772f959747612cbab36fda2d9475189d6f2d5546ec2dbe93"
]

and run analysis on it:

Code:
import re
from collections import Counter
import difflib


hashes = [
    "3d9d7ac2a7bf7a3755bede703a63a0fa17d0e206a5752555717432267fd9709e",
    "04e3b4023148040c4d000ff3493107317dc2eef513fe7bfbfbe706890c376846",
    "4e173939c30fccda068c5f959f7648c1d6a8625ba9edc6324dbdf74366d5f158",
    "2a108a45ed6ea108dfb8041e6f26df56d4c4b6dbf95184472212d50f8e951914",
    "4d4410cda3b52677d0f76087ab222d9fb2c3de8265b32660d6c9be0404938222",
    "fe8ff24859844caa772f959747612cbab36fda2d9475189d6f2d5546ec2dbe93"
]

def frequency_analysis(hashes):
    combined_hashes = ''.join(hashes)
    frequency = Counter(combined_hashes)
    return frequency

def find_doubles(hashes):
    doubles = []
    for h in hashes:
        doubles.extend(re.findall(r'(.)\1', h))
    return Counter(doubles)

def common_substrings(hashes, min_length=2):
    substrings = {}
    for i in range(len(hashes)):
        for j in range(i + 1, len(hashes)):
            matcher = difflib.SequenceMatcher(None, hashes[i], hashes[j])
            for block in matcher.get_matching_blocks():
                if block.size >= min_length:
                    substring = hashes[i][block.a:block.a + block.size]
                    if substring in substrings:
                        substrings[substring] += 1
                    else:
                        substrings[substring] = 1
    return {k: v for k, v in substrings.items() if v > 1}

def find_repeated_patterns(hashes):
    pattern_counts = Counter()
    for h in hashes:
        for match in re.findall(r'((.)\2+)', h):
            pattern_counts[match[0]] += 1
    return pattern_counts

frequency = frequency_analysis(hashes)
doubles = find_doubles(hashes)
common_subs = common_substrings(hashes)
repeated_patterns = find_repeated_patterns(hashes)

print("Frequency Analysis:")
print(frequency)
print("\nDouble Characters:")
print(doubles)
print("\nCommon Substrings:")
print(common_subs)
print("\nRepeated Patterns:")
print(repeated_patterns)

you will see something like this:

Code:
Frequency Analysis:
Counter({'d': 30, '2': 30, '4': 30, '7': 29, '6': 28, 'f': 27, '0': 27, '5': 25, '3': 23, '9': 22, 'e': 20, 'a': 19, 'b': 19, '1': 19, '8': 19, 'c': 17})

Double Characters:
Counter({'2': 4, '5': 3, '4': 3, 'f': 2, '6': 2, '7': 2, '0': 1, 'e': 1, 'c': 1, 'a': 1})

Common Substrings:
{'c2': 2, '0f': 3, 'be': 2, '76': 2, '93': 2, 'd6': 4, 'db': 2}

Repeated Patterns:
Counter({'44': 3, '55': 2, '22': 2, 'ff': 2, '66': 2, '77': 2, '222': 2, '555': 1, '000': 1, 'ee': 1, 'cc': 1, 'aa': 1})

Process finished with exit code 0

with this information you can see that theres bias in the randomness and leverage it like this simple example:
Code:
import subprocess
import os
import re

def create_custom_charsets(frequencies, patterns, substrings):
    """Creates custom character set files and a dictionary file.
        Args:
            frequencies: A list of characters in order of frequency
            patterns: A list of characters to create common patterns
            substrings: A list of common substrings
        Returns: None
    """
   
    # Use frequency analysis to create a character set with the most common characters.
    with open("chars_most_freq.txt", "w") as f:
       f.write("".join(frequencies))
    # Create a file with common patterns:
    with open("chars_patterns.txt", "w") as f:
        f.write("".join(patterns))
    # Create a dictionary file for common substrings:
    with open("substrings.txt", "w") as f:
      for substring in substrings:
         f.write(substring + "\n")

def attack_seed(hashed_seed, frequencies, patterns, substrings):
  """Attempts to crack a single hashed seed using different methods.
      Args:
         hashed_seed: The server seed hash
         frequencies: A list of characters in order of frequency
         patterns: A list of characters to create common patterns
         substrings: A list of common substrings
      Returns:
         True if the seed was found, False if not.
  """
  print(f"Attempting to crack: {hashed_seed}")

  create_custom_charsets(frequencies, patterns, substrings)

  # Define seed length
  seed_len = 64
  length_mask = "?1" * seed_len

  # ---- Attack Strategy ----
  # --- Try frequency attack first --
  print("Trying frequencies...")
  command = ["hashcat", "-a", "3", "-m", "0", hashed_seed, length_mask,
             "--custom-charset1", "chars_most_freq.txt", "--outfile", "cracked.txt", "--potfile-disable"]
  process = subprocess.run(command, capture_output=True, text=True)
  if re.search(rf'{hashed_seed}',process.stdout):
       print("SUCCESS: Seed found using frequency method.")
       return True

  # --- Try pattern attack next ---
  print("Trying patterns...")
  command = ["hashcat", "-a", "3", "-m", "0", hashed_seed, length_mask,
             "--custom-charset1", "chars_patterns.txt", "--outfile", "cracked.txt", "--potfile-disable"]
  process = subprocess.run(command, capture_output=True, text=True)
  if re.search(rf'{hashed_seed}',process.stdout):
      print("SUCCESS: Seed found using patterns method.")
      return True

  # --- Try hybrid with dictionary and frequency ---
  print("Trying hybrid with dictionary...")
  command = ["hashcat", "-a", "6", "-m", "0", "substrings.txt", length_mask,
             "--custom-charset1", "chars_most_freq.txt", "--outfile", "cracked.txt", "--potfile-disable"]
  process = subprocess.run(command, capture_output=True, text=True)
  if re.search(rf'{hashed_seed}',process.stdout):
       print("SUCCESS: Seed found using dictionary method.")
       return True

  print("Seed not found with current strategy. Expanding seed length and trying again.")

 #Iterate over seed lengths, up to the actual length of 64.
  for i in range(16, seed_len + 1, 16):
       length_mask = "?1" * i
       command = ["hashcat", "-a", "3", "-m", "0", hashed_seed, length_mask, "--custom-charset1",
                    "chars_most_freq.txt", "--outfile", "cracked.txt", "--potfile-disable"]
       process = subprocess.run(command, capture_output=True, text=True)

       if re.search(rf'{hashed_seed}',process.stdout):
           print(f"SUCCESS: Seed found at length {i} using frequency method.")
           return True

  for i in range(16, seed_len + 1, 16):
        length_mask = "?1" * i
        command = ["hashcat", "-a", "3", "-m", "0", hashed_seed, length_mask, "--custom-charset1",
                     "chars_patterns.txt", "--outfile", "cracked.txt", "--potfile-disable"]
        process = subprocess.run(command, capture_output=True, text=True)
        if re.search(rf'{hashed_seed}',process.stdout):
            print(f"SUCCESS: Seed found at length {i} using patterns method.")
            return True

  for i in range(16, seed_len + 1, 16):
         length_mask = "?1" * i
         command = ["hashcat", "-a", "6", "-m", "0", "substrings.txt", length_mask, "--custom-charset1",
                     "chars_most_freq.txt", "--outfile", "cracked.txt", "--potfile-disable"]
         process = subprocess.run(command, capture_output=True, text=True)
         if re.search(rf'{hashed_seed}',process.stdout):
             print(f"SUCCESS: Seed found at length {i} using dictionary method.")
             return True

  print("FAILURE: Seed not found.")
  return False

def main():
    """Main function to perform seed cracking."""
    print("Starting crack...")
    # Define the seed hashes
    hashed_seeds = [
        "de48f83cf2ae077ac4a988a28cffd94d9c1ed8253a8e850562a615b93e38329d",
        "824df4f901cc4ff2e80f0cfa9ae1413257457ec5fb744ad16d84d59e12b46a94",
        "8490c745afd237b5d79529af6a428d96315027daf34963440b363ccffa4f0cc4"
    ]

    #Define patterns and substrings
    frequencies = "d2476f0539eab18c"
    patterns = "d2476f0539eab18c4422ff6677"
    substrings = ["d6", "db", "c2", "0f"]

    #Attempt to crack the seeds
    for hashed_seed in hashed_seeds:
       attack_seed(hashed_seed, frequencies, patterns, substrings)
   
    #Cleanup created files.
    os.remove("chars_most_freq.txt")
    os.remove("chars_patterns.txt")
    os.remove("substrings.txt")
    if os.path.exists("cracked.txt"):
       os.remove("cracked.txt")

if __name__ == "__main__":
    main()


what does it reduce?
Code:
method is about 100,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000 times smaller or 1 followed by 48 zeros

this is a simple version of how we are not trying the full keyspace.

you could take it further
Code:
--- Validation ---
Hashes validated as hexadecimal strings of length 64.

--- Detailed Frequency Analysis ---
Overall Character Frequency (Counts):
 Counter({'d': 30, '2': 30, '4': 30, '7': 29, '6': 28, 'f': 27, '0': 27, '5': 25, '3': 23, '9': 22, 'e': 20, 'a': 19, 'b': 19, '1': 19, '8': 19, 'c': 17})

Overall Character Frequency (Percentages):
 {'3': '5.99%', 'd': '7.81%', '9': '5.73%', '7': '7.55%', 'a': '4.95%', 'c': '4.43%', '2': '7.81%', 'b': '4.95%', 'f': '7.03%', '5': '6.51%', 'e': '5.21%', '0': '7.03%', '6': '7.29%', '1': '4.95%', '4': '7.81%', '8': '4.95%'}

Positional Character Frequency (Position: {Character: Count}):
  Position 0: Counter({'4': 2, '3': 1, '0': 1, '2': 1, 'f': 1})
  Position 1: Counter({'d': 2, 'e': 2, '4': 1, 'a': 1})
  Position 2: Counter({'1': 2, '9': 1, 'e': 1, '4': 1, '8': 1})
  Position 3: Counter({'d': 1, '3': 1, '7': 1, '0': 1, '4': 1, 'f': 1})
  Position 4: Counter({'7': 1, 'b': 1, '3': 1, '8': 1, '1': 1, 'f': 1})
  Position 5: Counter({'a': 2, '4': 1, '9': 1, '0': 1, '2': 1})
  Position 6: Counter({'c': 2, '4': 2, '0': 1, '3': 1})
  Position 7: Counter({'2': 2, '9': 1, '5': 1, 'd': 1, '8': 1})
  Position 8: Counter({'a': 2, '3': 1, 'c': 1, 'e': 1, '5': 1})
  Position 9: Counter({'3': 2, '7': 1, '1': 1, 'd': 1, '9': 1})
  Position 10: Counter({'b': 2, '4': 1, '0': 1, '6': 1, '8': 1})
  Position 11: Counter({'f': 2, '8': 1, 'e': 1, '5': 1, '4': 1})
  Position 12: Counter({'7': 1, '0': 1, 'c': 1, 'a': 1, '2': 1, '4': 1})
  Position 13: Counter({'c': 2, 'a': 1, '4': 1, '1': 1, '6': 1})
  Position 14: Counter({'0': 2, '3': 1, 'd': 1, '7': 1, 'a': 1})
  Position 15: Counter({'7': 2, 'a': 2, 'c': 1, '8': 1})
  Position 16: Counter({'d': 2, '5': 1, '4': 1, '0': 1, '7': 1})
  Position 17: Counter({'5': 1, 'd': 1, '6': 1, 'f': 1, '0': 1, '7': 1})
  Position 18: Counter({'b': 2, '0': 1, '8': 1, 'f': 1, '2': 1})
  Position 19: Counter({'e': 1, '0': 1, 'c': 1, '8': 1, '7': 1, 'f': 1})
  Position 20: Counter({'0': 2, 'd': 1, '5': 1, '6': 1, '9': 1})
  Position 21: Counter({'f': 2, 'e': 1, '4': 1, '0': 1, '5': 1})
  Position 22: Counter({'9': 2, '7': 1, 'f': 1, '1': 1, '8': 1})
  Position 23: Counter({'7': 2, '0': 1, '3': 1, '5': 1, 'e': 1})
  Position 24: Counter({'4': 2, '3': 1, '9': 1, '6': 1, 'a': 1})
  Position 25: Counter({'f': 2, 'a': 1, '9': 1, 'b': 1, '7': 1})
  Position 26: Counter({'6': 2, '2': 2, '3': 1, '7': 1})
  Position 27: Counter({'1': 2, '6': 2, '3': 1, '2': 1})
  Position 28: Counter({'2': 2, 'a': 1, '0': 1, '4': 1, 'd': 1})
  Position 29: Counter({'0': 1, '7': 1, '8': 1, 'f': 1, 'd': 1, 'c': 1})
  Position 30: Counter({'f': 1, '3': 1, 'c': 1, '5': 1, '9': 1, 'b': 1})
  Position 31: Counter({'a': 2, '1': 2, '6': 1, 'f': 1})
  Position 32: Counter({'d': 2, 'b': 2, '1': 1, '7': 1})
  Position 33: Counter({'7': 1, 'd': 1, '6': 1, '4': 1, '2': 1, '3': 1})
  Position 34: Counter({'c': 3, 'd': 1, 'a': 1, '6': 1})
  Position 35: Counter({'0': 1, '2': 1, '8': 1, '4': 1, '3': 1, 'f': 1})
  Position 36: Counter({'e': 2, 'd': 2, '6': 1, 'b': 1})
  Position 37: Counter({'2': 2, 'e': 2, '6': 1, 'a': 1})
  Position 38: Counter({'0': 1, 'f': 1, '5': 1, 'd': 1, '8': 1, '2': 1})
  Position 39: Counter({'b': 2, '6': 1, '5': 1, '2': 1, 'd': 1})
  Position 40: Counter({'a': 2, '1': 1, 'f': 1, '6': 1, '9': 1})
  Position 41: Counter({'5': 2, '9': 2, '3': 1, '4': 1})
  Position 42: Counter({'7': 2, 'f': 1, 'e': 1, '5': 1, 'b': 1})
  Position 43: Counter({'5': 2, 'e': 1, 'd': 1, '1': 1, '3': 1})
  Position 44: Counter({'2': 2, '7': 1, 'c': 1, '8': 1, '1': 1})
  Position 45: Counter({'6': 2, '5': 1, 'b': 1, '4': 1, '8': 1})
  Position 46: Counter({'5': 1, 'f': 1, '3': 1, '4': 1, '6': 1, '9': 1})
  Position 47: Counter({'5': 1, 'b': 1, '2': 1, '7': 1, '0': 1, 'd': 1})
  Position 48: Counter({'7': 1, 'f': 1, '4': 1, '2': 1, 'd': 1, '6': 1})
  Position 49: Counter({'1': 1, 'b': 1, 'd': 1, '2': 1, '6': 1, 'f': 1})
  Position 50: Counter({'7': 1, 'e': 1, 'b': 1, '1': 1, 'c': 1, '2': 1})
  Position 51: Counter({'d': 2, '4': 1, '7': 1, '2': 1, '9': 1})
  Position 52: Counter({'3': 1, '0': 1, 'f': 1, 'd': 1, 'b': 1, '5': 1})
  Position 53: Counter({'5': 2, '2': 1, '6': 1, '7': 1, 'e': 1})
  Position 54: Counter({'4': 2, '0': 2, '2': 1, '8': 1})
  Position 55: Counter({'6': 2, '9': 1, '3': 1, 'f': 1, '4': 1})
  Position 56: Counter({'0': 2, '7': 1, '6': 1, '8': 1, 'e': 1})
  Position 57: Counter({'c': 2, 'f': 1, '6': 1, 'e': 1, '4': 1})
  Position 58: Counter({'d': 2, '9': 2, '3': 1, '2': 1})
  Position 59: Counter({'5': 2, '9': 1, '7': 1, '3': 1, 'd': 1})
  Position 60: Counter({'7': 1, '6': 1, 'f': 1, '1': 1, '8': 1, 'b': 1})
  Position 61: Counter({'0': 1, '8': 1, '1': 1, '9': 1, '2': 1, 'e': 1})
  Position 62: Counter({'9': 2, '4': 1, '5': 1, '1': 1, '2': 1})
  Position 63: Counter({'e': 1, '6': 1, '8': 1, '4': 1, '2': 1, '3': 1})

--- Detailed Double Character Analysis ---
Double Character Counts:
 Counter({'22': 4, '55': 3, '44': 3, 'ff': 2, '66': 2, '77': 2, '00': 1, 'ee': 1, 'cc': 1, 'aa': 1})

Double Character Positions (Double Char: {Position: Count}):
  Double '55': Counter({16: 1, 45: 1, 52: 1})
  Double '22': Counter({53: 1, 48: 1, 26: 1, 61: 1})
  Double '00': Counter({18: 1})
  Double 'ff': Counter({21: 1, 3: 1})
  Double 'ee': Counter({36: 1})
  Double 'cc': Counter({12: 1})
  Double '66': Counter({56: 1, 45: 1})
  Double '44': Counter({45: 1, 2: 1, 11: 1})
  Double '77': Counter({14: 1, 16: 1})
  Double 'aa': Counter({14: 1})

--- Top Common Substrings (min_length=3) ---
{'7bf': 1, 'e70': 1, '743': 1, '7d0': 1, '068': 1, '804': 1, '040': 1, '493': 1, 'f95': 1, 'cda': 1}

--- Top Repeated Patterns (min_length=2) ---
{'bf': 1, 'fb': 1, '39': 1, '04': 1}

--- Entropy ---
Shannon Entropy: 3.9733 bits per character

--- Trigram (3-gram) Analysis (Top 10) ---
{'f95': 3, '7bf': 2, 'e70': 2, '17d': 2, '7d0': 2, '743': 2, '267': 2, '804': 2, '040': 2, '493': 2}

--- Run Length Encoding (RLE) Analysis ---
Run Length Counts (lengths > 1):
 Counter({2: 16, 3: 4})

--- Character Type Distribution by Position ---
Character Type Counts per Position (Position: {Type: Count}):
  Position 0: Counter({'digit': 5, 'letter': 1})
  Position 1: Counter({'letter': 5, 'digit': 1})
  Position 2: Counter({'digit': 5, 'letter': 1})
  Position 3: Counter({'digit': 4, 'letter': 2})
  Position 4: Counter({'digit': 4, 'letter': 2})
  Position 5: Counter({'digit': 4, 'letter': 2})
  Position 6: Counter({'digit': 4, 'letter': 2})
  Position 7: Counter({'digit': 5, 'letter': 1})
  Position 8: Counter({'letter': 4, 'digit': 2})
  Position 9: Counter({'digit': 5, 'letter': 1})
  Position 10: Counter({'digit': 4, 'letter': 2})
  Position 11: Counter({'letter': 3, 'digit': 3})
  Position 12: Counter({'digit': 4, 'letter': 2})
  Position 13: Counter({'letter': 3, 'digit': 3})
  Position 14: Counter({'digit': 4, 'letter': 2})
  Position 15: Counter({'digit': 3, 'letter': 3})
  Position 16: Counter({'digit': 4, 'letter': 2})
  Position 17: Counter({'digit': 4, 'letter': 2})
  Position 18: Counter({'letter': 3, 'digit': 3})
  Position 19: Counter({'letter': 3, 'digit': 3})
  Position 20: Counter({'digit': 5, 'letter': 1})
  Position 21: Counter({'letter': 3, 'digit': 3})
  Position 22: Counter({'digit': 5, 'letter': 1})
  Position 23: Counter({'digit': 5, 'letter': 1})
  Position 24: Counter({'digit': 5, 'letter': 1})
  Position 25: Counter({'letter': 4, 'digit': 2})
  Position 26: Counter({'digit': 6})
  Position 27: Counter({'digit': 6})
  Position 28: Counter({'digit': 4, 'letter': 2})
  Position 29: Counter({'digit': 3, 'letter': 3})
  Position 30: Counter({'letter': 3, 'digit': 3})
  Position 31: Counter({'letter': 3, 'digit': 3})
  Position 32: Counter({'letter': 4, 'digit': 2})
  Position 33: Counter({'digit': 5, 'letter': 1})
  Position 34: Counter({'letter': 5, 'digit': 1})
  Position 35: Counter({'digit': 5, 'letter': 1})
  Position 36: Counter({'letter': 5, 'digit': 1})
  Position 37: Counter({'digit': 3, 'letter': 3})
  Position 38: Counter({'digit': 4, 'letter': 2})
  Position 39: Counter({'digit': 3, 'letter': 3})
  Position 40: Counter({'letter': 3, 'digit': 3})
  Position 41: Counter({'digit': 6})
  Position 42: Counter({'digit': 3, 'letter': 3})
  Position 43: Counter({'digit': 4, 'letter': 2})
  Position 44: Counter({'digit': 5, 'letter': 1})
  Position 45: Counter({'digit': 5, 'letter': 1})
  Position 46: Counter({'digit': 5, 'letter': 1})
  Position 47: Counter({'digit': 4, 'letter': 2})
  Position 48: Counter({'digit': 4, 'letter': 2})
  Position 49: Counter({'digit': 3, 'letter': 3})
  Position 50: Counter({'digit': 3, 'letter': 3})
  Position 51: Counter({'digit': 4, 'letter': 2})
  Position 52: Counter({'digit': 3, 'letter': 3})
  Position 53: Counter({'digit': 5, 'letter': 1})
  Position 54: Counter({'digit': 6})
  Position 55: Counter({'digit': 5, 'letter': 1})
  Position 56: Counter({'digit': 5, 'letter': 1})
  Position 57: Counter({'letter': 4, 'digit': 2})
  Position 58: Counter({'digit': 4, 'letter': 2})
  Position 59: Counter({'digit': 5, 'letter': 1})
  Position 60: Counter({'digit': 4, 'letter': 2})
  Position 61: Counter({'digit': 5, 'letter': 1})
  Position 62: Counter({'digit': 6})
  Position 63: Counter({'digit': 5, 'letter': 1})

--- Average Hamming Distance (Intra-Set) ---
Average Hamming Distance between hashes in the set: 60.07

--- Positional Character Set Diversity ---
Number of Unique Characters at Each Position (Position: Count):
{0: 5, 1: 4, 2: 5, 3: 6, 4: 6, 5: 5, 6: 4, 7: 5, 8: 5, 9: 5, 10: 5, 11: 5, 12: 6, 13: 5, 14: 5, 15: 4, 16: 5, 17: 6, 18: 5, 19: 6, 20: 5, 21: 5, 22: 5, 23: 5, 24: 5, 25: 5, 26: 4, 27: 4, 28: 5, 29: 6, 30: 6, 31: 4, 32: 4, 33: 6, 34: 4, 35: 6, 36: 4, 37: 4, 38: 6, 39: 5, 40: 5, 41: 4, 42: 5, 43: 5, 44: 5, 45: 5, 46: 6, 47: 6, 48: 6, 49: 6, 50: 6, 51: 5, 52: 6, 53: 5, 54: 4, 55: 5, 56: 5, 57: 5, 58: 4, 59: 5, 60: 6, 61: 6, 62: 5, 63: 6}

--- Matrix Representation (First 3 rows, for illustration) ---
Numerical Matrix Representation (Hex to Integer):
 [[ 3 13  9 13  7 10 12  2 10  7 11 15  7 10  3  7  5  5 11 14 13 14  7  0
   3 10  6  3 10  0 15 10  1  7 13  0 14  2  0  6 10  5  7  5  2  5  5  5
   7  1  7  4  3  2  2  6  7 15 13  9  7  0  9 14]
 [ 0  4 14  3 11  4  0  2  3  1  4  8  0  4  0 12  4 13  0  0  0 15 15  3
   4  9  3  1  0  7  3  1  7 13 12  2 14 14 15  5  1  3 15 14  7 11 15 11
  15 11 14  7  0  6  8  9  0 12  3  7  6  8  4  6]
 [ 4 14  1  7  3  9  3  9 12  3  0 15 12 12 13 10  0  6  8 12  5 15  9  5
   9 15  7  6  4  8 12  1 13  6 10  8  6  2  5 11 10  9 14 13 12  6  3  2
   4 13 11 13 15  7  4  3  6  6 13  5 15  1  5  8]]

------------------- End of Detailed Analytics -------------------
The point of my firmware is not doing this type of work for you its harnessing the massive power of the ASIC but its depending on you understanding how to reduce a keyspace
legendary
Activity: 2604
Merit: 2353
What are your sources or calcultations about the estimated amount of time for cracking un hash SHA-256? Especially "Seconds/Minutes" for an Antminer S9? If it was true, SHA-256 wouldn't be secured anymore and no one would use it. So I think if the sever seed is generated randomly and is a long string, you won't be able to find it with such method. It could only work if there are some rules, patterns or narrow bounds for generating the sever seed.
sr. member
Activity: 588
Merit: 273
This is for dice lovers.
I don't get how these things work.
If anyone wants to do this, it will not be for educational purposes but to cheat the system which is bad. No one will buy the Antminer S9 for this reason just to play for fun.
I am against any kind of system that cheats the system because they don't work.
brand new
Activity: 0
Merit: 0
Hey Bitcointalk!

I've developed a unique application for the Antminer S9 that goes beyond its typical use in Bitcoin mining. This project explores repurposing the S9's hashing power to analyze provably fair algorithms commonly used in online dice games.

Understanding Provably Fair

Many online dice games utilize provably fair algorithms to ensure transparency and fairness. These algorithms involve the server generating a secret seed, hashing it (e.g., with SHA-256), and sharing this hash with the player before the game begins. This allows players to verify the results independently.

The Antminer S9's Role

This project, available on GitHub (ProphetDiceBot/AntMinerProvablyFair), utilizes the Antminer S9's computational power to find the original seed that matches the provided hash. If successful, a player could potentially predict the game's outcome.

How it Works

 Server-Side: The server generates a random seed, hashes it, and provides the hashed seed to the player.
 Client-Side (with Antminer S9): The Antminer S9 brute-forces the SHA-256 hash to find the original seed.
 Prediction: If the Antminer S9 finds the original seed, the player can calculate the game's outcome in advance.

Example Code Snippet:

Code:
#include "asic.h"
#include
#include
#include
#include

// Define the target hash to compare against
const uint8_t target_hash[32] = {
    0x61, 0xe2, 0x89, 0xeb, 0x04, 0x7f, 0x73, 0x8c,
    0x2b, 0x02, 0xc4, 0x03, 0xcb, 0x2c, 0x60, 0x0c,
    0xb1, 0x15, 0x58, 0x68, 0xca, 0xbf, 0x6c, 0xb7,
    0xd3, 0xb9, 0x1f, 0xcb, 0x68, 0xa6, 0xec, 0x9c
};

// Function to generate candidates
void generate_candidate(uint64_t nonce, uint8_t *candidate) {
    memset(candidate, 0, 64); // Clear the buffer
    memcpy(candidate, &nonce, sizeof(nonce)); // Use nonce as input
}

// Function to print a hash as a hex string
void print_hash(const uint8_t *hash) {
    for (int i = 0; i < 32; i++) {
        printf("%02x", hash[i]);
    }
    printf("\n");
}

int main() {
    printf("Starting ProphetDice S9 Custom Firmware...\n");

    if (asic_init() != ASIC_OK) {
        printf("ASIC initialization failed!\n");
        return -1;
    }

    uint8_t candidate[64] = {0};
    uint8_t output[32] = {0};

    uint64_t nonce = 0;
    time_t start_time = time(NULL);

    while (1) {
        generate_candidate(nonce, candidate);

        if (asic_compute_hash(candidate, output) != ASIC_OK) {
            printf("Hash computation failed at nonce: %llu\n", nonce);
            asic_cleanup();
            return -1;
        }

        if (memcmp(output, target_hash, 32) == 0) {
            printf("Match found! Input: %llu\n", nonce);
            printf("Output hash: ");
            print_hash(output);
            break;
        }

        nonce++;
        if (nonce % 1000000 == 0) {
            printf("Checked %llu candidates...\n", nonce);
            time_t current_time = time(NULL);
            printf("Elapsed time: %ld seconds\n", current_time - start_time);
        }
    }

    asic_cleanup();
    return 0;
}

Important Considerations

 Computational Challenge: Cracking a SHA-256 hash is computationally demanding, even for the Antminer S9.
 Ethical Implications: This raises ethical questions about fair play.

Crucial Warning

This project is strictly for educational purposes. Do not attempt to use this on live gambling sites. Using it to predict game outcomes could be considered cheating and have severe consequences. Always gamble responsibly.

Performance Comparison

Metric | Python Script | Antminer S9
------- | -------- | --------
Hash Rate | 100 MH/s | 13.5 TH/s
Time to Crack | Forever | Seconds/Minutes

Disclaimer

This project demonstrates the capabilities of ASIC hardware. It's essential to use this knowledge ethically and responsibly.

Feel free to reach out for questions or assistance. All code is provided open source.

Remember: Knowledge is power, but it's crucial to wield it responsibly.

There goes the neighborhood
Pages:
Jump to: