Author

Topic: Metodologia de Cálculo dos Sorteios (Read 314 times)

staff
Activity: 1285
Merit: 1085
December 05, 2020, 05:14:29 PM
#12
Apenas uma pequena atualização sobre a versão final que está sendo utilizada no sorteio de fim de ano 2020:

Ao invés de escolher os números de trás pra frente, a escolha segue a ordem normal. Abaixo o novo código para validação, onde a única alteração é a remoção da função reversed:



Como validar as apostas e sorteios

Qualquer pessoa pode utilizar o seguinte script para validar tanto as apostas quanto o sorteio.
Talvez você já tenha python instalado em seu computador, pode verificar digitando python --version em um terminal. Outra alternativa é utilizar um site online, por exemplo https://repl.it/languages/python3

A seguir, o script:

Code:
from collections import namedtuple
import hashlib
import hmac
import sys


class Utils:
    choices = 32
    pick = 4

    @staticmethod
    def calculate_numbers(first_hash, second_hash, how_many=1, checksum=None):
        if checksum:
            calculated_checksum = hashlib.sha256(second_hash.encode()).hexdigest()
            if calculated_checksum != checksum:
                print("Provided checksum does not match. Please check it!!!")
                return
            else:
                print("Checksum validation OK!")

        raw_hash = hmac.new(first_hash.encode(), second_hash.encode(), digestmod=hashlib.sha256)
        for n in range(how_many):
            encoded_hash = raw_hash.hexdigest()
            result = Utils.convert_hash_to_numbers(encoded_hash)
            print(f"{n} - {encoded_hash} = {Utils.get_numbers([i.number for i in result])}")
            Utils.print_formatted([str(i.segment) for i in result])
            Utils.print_formatted([str(i.decimal) for i in result])
            Utils.print_formatted([str(i.number) for i in result])
            raw_hash.update(str(n).encode())
            print()

    @staticmethod
    def print_formatted(value):
        print("\t", end="")
        for i in value:
            print(f"{i:>3s} ", end="")
        print("")

    @staticmethod
    def get_numbers(numbers):
        result = []
        for n in numbers:
            if n not in result:
                result.append(n)
            if len(result) == Utils.pick:
                return result

    @staticmethod
    def convert_hash_to_numbers(h):
        Chunk = namedtuple('Chunk', ['segment', 'decimal', 'number'])
        return [Chunk(i, int(i, 16), int(i, 16) % Utils.choices) for i in Utils.chunk_string(h, 2)]

    @staticmethod
    def chunk_string(string, length):
        return (string[0+i:length+i] for i in range(0, len(string), length))


if __name__ == '__main__':
    print_help = True
    try:
        if len(sys.argv) == 5:
            if sys.argv[1] == 'draw':
                print_help = False
                print("Draw Validation")
                Utils.calculate_numbers(first_hash=sys.argv[2], second_hash=sys.argv[3], checksum=sys.argv[4])
            elif sys.argv[1] == 'ticket':
                print_help = False
                print("Ticket Validation")
                Utils.calculate_numbers(first_hash=sys.argv[2], second_hash=sys.argv[3], how_many=int(sys.argv[4]))
        else:
            print("Incorrect number of parameters.")
    except IndexError:
        pass
    finally:
        if print_help:
            print(f"Syntax:\n\t{sys.argv[0]} draw \n"
                  f"\t{sys.argv[0]} ticket ")
...
legendary
Activity: 2352
Merit: 1121
☢️ alegotardo™️
February 13, 2020, 04:51:23 PM
#11
~snip~

Espero que tenha ficado mais claro, mas qualquer coisa dá um grito.

Ahhh, pior que nem era tão complicado assim de entender no final das contas.
Consegui replicar os resultados, script auditado e aprovado Cheesy

Quanto a escolha dos números, a única forma de fazer isso de forma transparente e automatizada seria escrevendo eles na própria blockchain, mas aí infelizmente teríamos que partir para outra Coin afim de tornar isso possível para qualquer "mortal", talvez até um SmartContract na ETH ou algo do tipo.

[EDIT]
Vide aqui um tutorial produzido pelo @Loganota que pode cair muito bem como uma solução para a escolha dos números, ou não?
hero member
Activity: 1498
Merit: 556
February 13, 2020, 02:33:07 PM
#10
Sweet! Muito bem explicado, Adriano! E, como já havia mencionado no tópico anterior, gostei da metodologia. Concordo que com o tempo algumas melhorias poderiam ser feitas (o suporte à LN e a escolha dos números que você cogitou viriam bem a calhar... apesar que neste último nunca tive muita sorte, talvez a aleatoriedade seja o melhor caminho para alguns ainda... aliás, diria até que isso é um complô para diminuir minha taxa de assertividade depois da dobradinha na virada do ano Grin).

Aproveito, ainda, e deixo a sugestão de incluir um ponto aqui: caso a transação não seja confirmada em tempo hábil ou a transação ocorra em um período que anteceda o anúncio de novos sorteios, o montante passaria a compor o próximo pote. Penso que esse seria o caminho natural, mas acho interessante destacar para não restarem dúvidas quando houver alguma ocorrência do gênero.

Ah e espero mesmo que seja uma iniciativa de sucesso ao ponto de chegarmos no suposto cenário:

(...)
Imagine que um grande minerador poderia verificar o hash gerado para o bloco ganhador e decidir não publicar aquele esperando gerar um bloco que o beneficiasse
(...)

 Grin Cool
staff
Activity: 1285
Merit: 1085
February 13, 2020, 11:37:06 AM
#9
Boa Adriano,
Muito bem esclarecido.

Mas como eu sou burro, ainda não consegui chegar ao HEX correto Grin

Com certeza tem algo que eu estou esquecendo, mesmo acompanhando o código não consegui entender e chegar ao resultado, vamos lá:

Code:
Hash do bloco: 000000000000000000054faed5985e33f3281604547ad22bd035785ca8ec20d8
Hash da TX: caf02fb319e38b7d6bceafcda0e6f8720ebf8dcd14a1f2632fcd7baddf85a1b1
SHA256 em cima de: 000000000000000000054faed5985e33f3281604547ad22bd035785ca8ec20d8caf02fb319e38b7d6bceafcda0e6f8720ebf8dcd14a1f2632fcd7baddf85a1b1
Resultado: cfac14c0a41fd19eef1a7b77bb848f887bba6662daba98a231b82d621beba38b

Creio que deve ter mais algum parâmetro aí e que ele seja o número do ticket que é necessário para as apostas múltiplas, mas em nenhum dos meus testes consegui encaixar ele e obter o Hex correto.

Ah, o Script tá ok... os resultados dos tickets e do raw batem com a sua explanação. O problema sou eu mesmo Tongue

Oi Ale, não se preocupe. O problema é que o processo é um pouco mais complicado do que só concatenar as duas informações juntas:
O processo para gerar o hash pode ser explicado como assinar uma mensagem (o hash da transação) com uma chave (o hash do bloco)... O resultado é o primeiro ticket, para gerar os demais, nós adicionamos um sequencial (1, 2, 3) ao final do hash da transação e repetimos o processo quantas vezes for necessária para gerar o número de tickets necessarios. Para o sorteio é a mesma coisa, mas ao invés da transação e bloco são utilizados o bloco e a sequência de caracteres secreta.

Esse site permite você fazer o mesmo cálculo para chegar no resultado correto: https://www.freeformatter.com/hmac-generator.html (tem vários outros, esse foi o primeiro que achei numa busca rápida)

Espero que tenha ficado mais claro, mas qualquer coisa dá um grito.

Abraço,
Adriano
legendary
Activity: 2352
Merit: 1121
☢️ alegotardo™️
February 13, 2020, 08:42:50 AM
#8
Boa Adriano,
Muito bem esclarecido.

Mas como eu sou burro, ainda não consegui chegar ao HEX correto Grin

Com certeza tem algo que eu estou esquecendo, mesmo acompanhando o código não consegui entender e chegar ao resultado, vamos lá:

Code:
Hash do bloco: 000000000000000000054faed5985e33f3281604547ad22bd035785ca8ec20d8
Hash da TX: caf02fb319e38b7d6bceafcda0e6f8720ebf8dcd14a1f2632fcd7baddf85a1b1
SHA256 em cima de: 000000000000000000054faed5985e33f3281604547ad22bd035785ca8ec20d8caf02fb319e38b7d6bceafcda0e6f8720ebf8dcd14a1f2632fcd7baddf85a1b1
Resultado: cfac14c0a41fd19eef1a7b77bb848f887bba6662daba98a231b82d621beba38b

Creio que deve ter mais algum parâmetro aí e que ele seja o número do ticket que é necessário para as apostas múltiplas, mas em nenhum dos meus testes consegui encaixar ele e obter o Hex correto.

Ah, o Script tá ok... os resultados dos tickets e do raw batem com a sua explanação. O problema sou eu mesmo Tongue
staff
Activity: 1285
Merit: 1085
February 13, 2020, 07:16:09 AM
#7
Próximos passos
Fiquem a vontade de questionar qualquer ponto que não esteja claro... O assunto é pesado para quem não tem conhecimento de programação então qualquer dúvida vai ajudar a explicar melhor o processo... E se você quiser aproveitar a oportunidade para aprender um pouco, python é relativamente simples de aprender e o script fornecido acima tem apenas 77 linhas, sem utilizar nenhuma biblioteca externa, pode ser um bom inicio Cheesy

Caso não surja nenhum problema grave na validação da idéia proposta, o próximo passo será abrir as apostas para o primeiro sorteio, visando um bloco da quarta-feira de cinzas (pra curar a ressaca do carnaval, pelo menos para os ganhadores). Os seguintes poderiam ser: Páscoa, Tiradentes, Dia do Trabalho ou Dia das Mães (ou Bitcoin Halving que deve ser bem próximo do dia das mães). Com esses concursos já poderemos fazer algumas pequenas alterações e ver se a idéia pega.
staff
Activity: 1285
Merit: 1085
February 13, 2020, 07:16:03 AM
#6
Alguns comentários
 - A escolha dos números a partir do final ou do começo não faz diferença, eu só estou utilizando a partir do final porque no plano inicial eu iria utilizar apenas o hash do bloco e os primeiros caracteres são sempre 0 (e a cada aumento de dificuldade mais caracteres são 0)
 - A introdução da sequência aleatória de caracteres tem dois propósitos: 1) resolver o problema da dificuldade fazendo o hash tender a 0; 2) Evitar que um alguém com recursos manipule o sorteio. Imagine que um grande minerador poderia verificar o hash gerado para o bloco ganhador e decidir não publicar aquele esperando gerar um bloco que o beneficiasse - é um cenário irreal hoje em dia, mas imagine que o pote do sorteio cresça bastante, enquanto nós sabemos que a recompensa por novos blocos diminui pela metade a cada 4 anos (210000 blocos para falar a verdade)
 - A quantidade de números válidos (de 0 a 31) é importante... Esse método só funciona com as seguintes opções de números válidos: (2, 4, 8, 16, 32, 64, 128 e 256). Isso porque se um número diferente for utilizado, teríamos o mesmo efeito de um dado viciado no sorteio: alguns números teriam mais chance de sair do que outros. Por exemplo, se utilizássemos 10 números, os números de 0 a 5 teriam cada um 26 chances de sair, porém de 6 a 9 teriam apenas 25 chances cada.
- Tenho interesse em achar uma solução para manter a transparência e reproducibilidade (se é que isso é uma palavra válida) que permita ao apostador escolher os números, se possível incorporando lightning... sugestões são bem vindas.
- As chances de acertar os 4 números são relativamente baixas, então imagino que o valor acumule com frequência. Isso fará com que os prêmios de 3 e 2 acertos fiquem bem "interessantes" rapidamente, servindo de incentivo para termos mais apostadores.
staff
Activity: 1285
Merit: 1085
February 13, 2020, 07:15:56 AM
#5
Como validar as apostas e sorteios

Qualquer pessoa pode utilizar o seguinte script para validar tanto as apostas quanto o sorteio.
Talvez você já tenha python instalado em seu computador, pode verificar digitando python --version em um terminal. Outra alternativa é utilizar um site online, por exemplo https://repl.it/languages/python3

A seguir, o script:

Code:
from collections import namedtuple
import hashlib
import hmac
import sys


class Utils:
    choices = 32
    pick = 4

    @staticmethod
    def calculate_numbers(first_hash, second_hash, how_many=1, checksum=None):
        if checksum:
            calculated_checksum = hashlib.sha256(second_hash.encode()).hexdigest()
            if calculated_checksum != checksum:
                print("Provided checksum does not match. Please check it!!!")
                return
            else:
                print("Checksum validation OK!")

        raw_hash = hmac.new(first_hash.encode(), second_hash.encode(), digestmod=hashlib.sha256)
        for n in range(how_many):
            encoded_hash = raw_hash.hexdigest()
            result = Utils.convert_hash_to_numbers(encoded_hash)
            print(f"{n} - {encoded_hash} = {Utils.get_numbers([i.number for i in result])}")
            Utils.print_formatted([str(i.segment) for i in result])
            Utils.print_formatted([str(i.decimal) for i in result])
            Utils.print_formatted([str(i.number) for i in result])
            raw_hash.update(str(n).encode())
            print()

    @staticmethod
    def print_formatted(value):
        print("\t", end="")
        for i in value:
            print(f"{i:>3s} ", end="")
        print("")

    @staticmethod
    def get_numbers(numbers):
        result = []
        for n in reversed(numbers):
            if n not in result:
                result.append(n)
            if len(result) == Utils.pick:
                return result

    @staticmethod
    def convert_hash_to_numbers(h):
        Chunk = namedtuple('Chunk', ['segment', 'decimal', 'number'])
        return [Chunk(i, int(i, 16), int(i, 16) % Utils.choices) for i in Utils.chunk_string(h, 2)]

    @staticmethod
    def chunk_string(string, length):
        return (string[0+i:length+i] for i in range(0, len(string), length))


if __name__ == '__main__':
    print_help = True
    try:
        if len(sys.argv) == 5:
            if sys.argv[1] == 'draw':
                print_help = False
                print("Draw Validation")
                Utils.calculate_numbers(first_hash=sys.argv[2], second_hash=sys.argv[3], checksum=sys.argv[4])
            elif sys.argv[1] == 'ticket':
                print_help = False
                print("Ticket Validation")
                Utils.calculate_numbers(first_hash=sys.argv[2], second_hash=sys.argv[3], how_many=int(sys.argv[4]))
        else:
            print("Incorrect number of parameters.")
    except IndexError:
        pass
    finally:
        if print_help:
            print(f"Syntax:\n\t{sys.argv[0]} draw \n"
                  f"\t{sys.argv[0]} ticket ")

Para validar os tickets, é necessário fornecer o hash do bloco em que a transação foi incluída na blockchain, o hash da transação e o número de tickets a serem gerados (uma transação pode gerar mais de um ticket se for enviada uma quantidade múltipla do preço de um ticket)

De acordo com o exemplo dado acima, a transação caf02fb319e38b7d6bceafcda0e6f8720ebf8dcd14a1f2632fcd7baddf85a1b1 foi incluída no bloco
608637, cujo hash é 000000000000000000054faed5985e33f3281604547ad22bd035785ca8ec20d8.
O valor enviado foi 0.002 e é o equivalente a dois tickets.

Rodando o script com esses parâmetros temos o seguinte resultado:

Code:
$ python validate_lottery.py ticket 000000000000000000054faed5985e33f3281604547ad22bd035785ca8ec20d8 caf02fb319e38b7d6bceafcda0e6f8720ebf8dcd14a1f2632fcd7baddf85a1b1 2
Ticket
0 - 5cc6992fc5006c21d788635354515682332e45cbaba6178bf61a8a0a5a88fd75 = [21, 29, 8, 26]
5c  c6  99  2f  c5  00  6c  21  d7  88  63  53  54  51  56  82  33  2e  45  cb  ab  a6  17  8b  f6  1a  8a  0a  5a  88  fd  75
92 198 153  47 197   0 108  33 215 136  99  83  84  81  86 130  51  46  69 203 171 166  23 139 246  26 138  10  90 136 253 117
28   6  25  15   5   0  12   1  23   8   3  19  20  17  22   2  19  14   5  11  11   6  23  11  22  26  10  10  26   8  29  21


1 - b8940fbeee0c5140ab4663715eb93c8e2832ff7aa2391a9dc67b28e7bf4499ea = [10, 25, 4, 31]
b8  94  0f  be  ee  0c  51  40  ab  46  63  71  5e  b9  3c  8e  28  32  ff  7a  a2  39  1a  9d  c6  7b  28  e7  bf  44  99  ea
184 148  15 190 238  12  81  64 171  70  99 113  94 185  60 142  40  50 255 122 162  57  26 157 198 123  40 231 191  68 153 234
24  20  15  30  14  12  17   0  11   6   3  17  30  25  28  14   8  18  31  26   2  25  26  29   6  27   8   7  31   4  25  10

As linha iniciando com "0 - " e "1 - " são o que realmente importa, elas indicam o número do ticket, o valor aleatório gerado para esse ticket e ao final, após o sinal de "=", os 4 números com os quais o ticket estará concorrendo.

O script fornecido anteriormente mostra em detalhe o processo de geração desses números, mas em linhas gerais:

- Os hashes do bloco e da transação são combinados utilizando sha256, essa sequência de caracteres possui 32 bytes (cada 2 caracteres aqui representam um número em hexadecimal - que varia de 00 a FF). Vvamos pegar o ticket 0 como exemplo:
Code:
5cc6992fc5006c21d788635354515682332e45cbaba6178bf61a8a0a5a88fd75
acima,
- A primeira linha após o ticket mostra essa divisão de 2 em 2 caracteres:
Code:
5c  c6  99  2f  c5  00  6c  21  d7  88  63  53  54  51  56  82  33  2e  45  cb  ab  a6  17  8b  f6  1a  8a  0a  5a  88  fd  75
- A segunda linha apenas converteu os números de hexadecimal para decimal (aqui tem uma calculadora, assim como uma explicação para aqueles que tenham interesse: https://calculareconverter.com.br/converter-hexadecimal-para-decimal/ )
Code:
92 198 153  47 197   0 108  33 215 136  99  83  84  81  86 130  51  46  69 203 171 166  23 139 246  26 138  10  90 136 253 117
- A terceira linha contém o resto da divisão de cada número por 32. Isso "normaliza" o resultado, fazendo com que tenhamos apenas números de 0 a 31. As chances de cada número aparecer são as mesmas porque 256 (o total de números válidos inicialmente) é múltiplo de 32:
Code:
28   6  25  15   5   0  12   1  23   8   3  19  20  17  22   2  19  14   5  11  11   6  23  11  22  26  10  10  26   8  29  21

Finalmente, começando do final da sequência anterior, pegamos os primeiros 4 números não repetidos, resultando nos números 21, 29, 8, 26

Para validar o sorteio, o processo é exatamente o mesmo, porém os parâmetros são o hash do bloco final, a sequência de caracteres e o hash informado no post inicial (esse hash será utilizado para confirmar que a sequência de caracteres não foi alterada. Vamos ao exemplo:

Code:
$ python validate_lottery.py draw 0000000000000000000465da70a3247e619e478ae45bf048e33a03a09a0f8dc2 eu-ainda-nao-sei-qual-a-palavra 95ec1d26f124c8d08323eaae796cd75f7ac13d93caab725ac6776f721af8f
Draw Validation
Provided checksum does not match. Please check it!!!

Veja que como eu ainda não sabia a sequência de caracteres, a validação falhou visto que o hash 95ec1d26f124c8d08323eaae796cd75f7ac13d93caab725ac6776f721af8f não corresponde ao que eu forneci ao script(eu-ainda-nao-sei-qual-a-palavra). Neste caso nenhum detalhe foi gerado sobre os números sorteados.

Vamos agora tentar novamente com a sequência correta:

Code:
$ python validate_lottery.py draw 0000000000000000000465da70a3247e619e478ae45bf048e33a03a09a0f8dc2 4b95d84c-f41f-4eea-9f49-e7d147d4fb59 95ec1d26f124c8d08323eaae796cd75f7ac13d93caab725ac6776f721af8fea3
Draw Validation
Checksum validation OK!
0 - cc08162c81e682220e33e12cf55dfa0fa296500994fdd4c3e2e998f84cdc0a98 = [24, 10, 28, 12]
cc  08  16  2c  81  e6  82  22  0e  33  e1  2c  f5  5d  fa  0f  a2  96  50  09  94  fd  d4  c3  e2  e9  98  f8  4c  dc  0a  98
204   8  22  44 129 230 130  34  14  51 225  44 245  93 250  15 162 150  80   9 148 253 212 195 226 233 152 248  76 220  10 152
12   8  22  12   1   6   2   2  14  19   1  12  21  29  26  15   2  22  16   9  20  29  20   3   2   9  24  24  12  28  10  24

Desta vez recebemos a mensagem que o checksum foi validado ok (Checksum validation OK!) e logo a seguir o resultado do sorteio (números 24, 10, 28 e 12) seguidos pelo detalhamento de como o número foi gerado, da mesma maneira que para a geração dos tickets (separação 2 a 2, conversão hexadecimal -> decimal, "normalização" dos números pegando o resto da divisão por 32 e finalmente pegando os 4 números não repetidos a partir do final)
staff
Activity: 1285
Merit: 1085
February 13, 2020, 07:09:26 AM
#4
Bloco do Sorteio Gerado - Sorteio
Após a geração do bloco, eu divulgarei a sequência de caracteres utilizada em conjunto com o bloco para geração dos números ganhadores. Nesse momento, qualquer pessoa pode utilizar o script de verificação para confirmar os números ganhadores e que a sequência de caracteres corresponde ao hash informado no primeiro dia.
Lembrando que se forem gerados dois ou mais blocos com o mesmo número, os blocos que ficarem orfãos não são válidos
staff
Activity: 1285
Merit: 1085
February 13, 2020, 07:09:19 AM
#3
A partir da abertura até o último dia - Apostas
Apostas podem ser feitas normalmente do momento da abertura do sorteio até 3 blocos antes do sorteio (sua transação precisa ter 3 confirmações para concorrer ao sorteio)
Durante esse período, qualquer usuário pode verificar as transações recebidas no endereço de apostas e validar os tickets gerados com o script fornecido neste tópico.
staff
Activity: 1285
Merit: 1085
February 13, 2020, 07:09:11 AM
#2
Primeiro dia - Abertura de um novo sorteio
Nesse dia, será postada por mim uma mensagem com os detalhes do sorteio, contendo o número do bloco utilizado para o sorteio, valor de cada ticket, hash da sequência de caracteres que será utilizada para o cálculo do sorteio e a forma de distribuição dos valores arrecadados.


Exemplo (IMPORTANTE: NÃO faça depósitos nesse endereço pois o mesmo foi utilizado em um outro sorteio e não é válido):


Sorteio baseado no bloco: 610614
Endereço para apostas: bc1qak3ruahryeplk86z697r-sf5828w-44tu6t-2ph5hqn380x43emqu83duk
Valor do ticket: 0.001 BTC
Hash da sequência de caracteres: 95ec1d26f124c8d08323eaae796cd75f7ac13d93caab725ac6776f721af8fea3

Distribuição dos prêmios:

Número de AcertosPercentualProbabilidade de Acerto (1 em)
428%35960
314% 321.07
2 7% 15.86

  • Havendo mais de um acertador em uma faixa de premiação, o prêmio será dividido entre todos os acertadores.
  • O saldo restante ficará acumulado para o próximo sorteio.
  • Bonus Inicial: Tickets que não recebam nenhum prêmio serão válidos no próximo sorteio
staff
Activity: 1285
Merit: 1085
February 12, 2020, 08:25:55 PM
#1
Esse tópico visa discutir os detalhes técnicos, algoritmo e assuntos correlatos à metodologia de cálculo que adotei para os sorteios do fórum (post inicial: Sorteio de Carnaval).

Antes de começar o detalhamento, um resumo de como será a dinâmica dos sorteios:

A seguir o detalhamento do processo, com código de exemplo para validar que a geração dos tickets e sorteio são justos:

E os passos a seguir, após a discussão inicial:



Abraço,
Adriano
Jump to: