Assinatura

A prova de que você é o dono de uma chave pública

Diagrama mostrando uma entrada sendo destravada ao fornecer uma assinatura que corresponde à chave pública dentro de uma saída.

Uma assinatura é usada para provar que você é o dono de uma chave pública.

Ela é usada dentro das transações para destravar saídas que foram travadas a uma chave pública.

Uma assinatura é criada usando a chave privada que foi usada para criar a chave pública. A assinatura tem, portanto, uma conexão matemática única com a chave pública, o que é suficiente para provar que você é o "dono" da chave pública sem ter que revelar a chave privada original.

Ícone Ferramenta Assinar com ECDSA

Assinatura ECDSA

Assine o hash de uma mensagem usando uma chave privada.

Normalmente é o hash de dados de uma transação (já preparados para assinatura)

0x
0 bytes
0x
0x
0 bytes
Assinatura
0d
0d
High: Low:

Nunca insira sua chave privada em um site, nem use uma chave privada gerada por um site. Sites podem facilmente salvar a chave privada e usá-la para roubar seus bitcoins.

Ícone Ferramenta Assinar com Schnorr

Assinatura Schnorr

Assine uma mensagem usando uma chave privada.

0x
0 bytes
0x
0 bytes
0x
0 bytes
0x
0 bytes

Nunca insira sua chave privada em um site, nem use uma chave privada gerada por um site. Sites podem facilmente salvar a chave privada e usá-la para roubar seus bitcoins.

Criando

Como você assina uma transação de bitcoin?

Diagrama mostrando uma assinatura sendo criada ao assinar os dados da transação usando uma chave privada.

Para criar uma assinatura, você precisa assinar uma mensagem usando a sua chave privada.

No Bitcoin, essa mensagem são os dados da transação que contêm a entrada que você quer destravar.

Os passos básicos para assinar uma transação são:

  1. Construir a transação. Esta será a mensagem que assinamos.
  2. Usar a sua chave privada para assinar a transação.
  3. Colocar a assinatura de volta dentro da transação.

Repita esses passos para cada entrada que você quiser assinar.

Porém, antes de podermos criar a assinatura, precisamos preparar os dados da transação para a assinatura.

Há três métodos diferentes para assinar uma transação, e o "algoritmo de assinatura" que você usa depende do tipo de script de travamento da saída que você quer gastar:

Algoritmo Legado

O algoritmo de assinatura legado é usado para criar assinaturas que destravam scripts de travamento não-segwit, como: P2PK, P2PKH, P2MS e P2SH.

Ou seja, basicamente, toda vez que um script de travamento contém OP_CHECKSIG e não é um P2WPKH, P2WSH ou P2TR, use este algoritmo de assinatura legado.

0. Criar a transação

Ícone Ferramenta Construtor de Transação

Construtor de Transações

Monte os dados brutos de uma transação.

Tipo

Versão

0d
Transação Básica

Entradas (1)

Saídas (1)

Locktime

0d
Altura de Bloco

Dados Brutos da Transação

0 bytes 0 vbytes
Ícone Ferramenta Separador de Transação

Separador de Transações

Cole uma transação bruta (hexadecimal) para dividi-la nos seus campos individuais.

Diagrama mostrando a estrutura visual de uma transação de bitcoin não assinada.

Para começar, você precisa criar alguns dados de transação que gastem bitcoins que você possui.

É basicamente aqui que você seleciona as entradas que quer gastar e então cria as saídas a que quer travar essas entradas gastas. Em outras palavras, por enquanto você está apenas descrevendo a movimentação das moedas.

É assim que se parece a minha transação bruta:

Transação Bruta (Não Assinada):

version: 01000000
inputs:  01
  txid: b7994a0db2f373a29227e1d90da883c6ce1cb0dd2d6812e4558041ebbbcfa54b
  vout: 00000000
  scriptsigsize: 00
  scriptsig:
  sequence: ffffffff
outputs: 01
  amount: 983a000000000000
  scriptpubkeysize: 19
  scriptpubkey: 76a914b3e2819b6262e0b1f19fc7229d75677f347c91ac88ac
locktime: 00000000

Os TXIDs dentro dos dados da transação bruta estão em ordem de bytes invertida.

Aqui eu selecionei os bitcoins que quero gastar referenciando o TXID e um VOUT específico de uma transação anterior. Esta é a entrada.

Em seguida, criei uma nova saída contendo um amount de bitcoins com um novo travamento ScriptPubKey (este é um travamento P2PKH, mas isso não importa).

Mais importante, note que o ScriptSig da entrada está vazio. É aqui que a nossa assinatura vai ficar, e precisamos criar uma se quisermos conseguir enviar esta transação para a rede bitcoin.

1. Remover os ScriptSigs existentes

Não assinamos nenhum dado de ScriptSig existente ao assinar uma transação.

Então, se você já assinou outras entradas e criou ScriptSigs (scripts de desbloqueio) para elas, remova-os temporariamente da transação.

Na minha transação estou destravando apenas uma entrada, então não preciso remover nenhum ScriptSig existente.

Você também precisa remover quaisquer dados de marcador, flag e testemunha da transação. Isto só se aplica se você estiver fazendo uma transação segwit (ou seja, destravando um P2WPKH ou P2WSH na mesma transação).

2. Colocar o ScriptPubKey como placeholder no ScriptSig

Diagrama mostrando o ScriptPubKey da saída sendo gasta sendo usado como placeholder dentro do ScriptSig da entrada.

Antes de assinar uma entrada legada, precisamos colocar o ScriptPubKey da saída que queremos gastar no ScriptSig da nossa entrada.

Por exemplo, olhando os dados da transação bruta da saída que queremos gastar, vemos que ela tem um script de travamento P2PKH de:

76a9144299ff317fcd12ef19047df66d72454691797bfc88ac

Então colocamos isto no scriptsig como placeholder:

Transação Bruta (Não Assinada):

version: 01000000
inputs:  01
  txid: b7994a0db2f373a29227e1d90da883c6ce1cb0dd2d6812e4558041ebbbcfa54b
  vout: 00000000
  scriptsigsize: 19
  scriptsig: 76a9144299ff317fcd12ef19047df66d72454691797bfc88ac
  sequence: ffffffff
outputs: 01
  amount: 983a000000000000
  scriptpubkeysize: 19
  scriptpubkey: 76a914b3e2819b6262e0b1f19fc7229d75677f347c91ac88ac
locktime: 00000000
  • O placeholder no scriptsig da transação atual agora contém o scriptpubkey da saída da transação anterior.
  • O scriptpubkey da saída na transação atual é um novo script de travamento.

O placeholder também ajusta temporariamente o valor de scriptsigsize.

Fazemos isto para indicar a entrada específica que estamos assinando e que estamos cientes do script de travamento original da saída que pretendemos gastar.

3. Anexar o tipo de hash da assinatura aos dados da transação

Agora temos que nos comprometer com quanto da estrutura da transação vamos assinar. Fazemos isso anexando um tipo de hash da assinatura aos dados atuais da transação.

O mais comum é o SIGHASH_ALL (0x01), que indica que a assinatura cobre todas as entradas e saídas da transação. Isso significa que ninguém mais pode adicionar entradas ou saídas a ela depois.

Então é assim que se parece a nossa transação bruta não assinada agora:

Transação Bruta (Não Assinada):

version: 01000000
inputs:  01
  txid: b7994a0db2f373a29227e1d90da883c6ce1cb0dd2d6812e4558041ebbbcfa54b
  vout: 00000000
  scriptsigsize: 19
  scriptsig: 76a9144299ff317fcd12ef19047df66d72454691797bfc88ac
  sequence: ffffffff
outputs: 01
  amount: 983a000000000000
  scriptpubkeysize: 19
  scriptpubkey: 76a914b3e2819b6262e0b1f19fc7229d75677f347c91ac88ac
locktime: 00000000
sighash: 01000000

O sighash, quando anexado aos dados da transação, tem 4 bytes e está em ordem de bytes little-endian.

4. Hashear os dados da transação

Agora que preparamos a transação bruta não assinada, podemos criar um hash dela pronta para assinatura.

O Bitcoin usa SHA-256 duplo ao hashear coisas (também chamado de hash HASH256). Então, se serializarmos a nossa transação não assinada atual e a hashearmos, obtemos:

message: 0100000001b7994a0db2f373a29227e1d90da883c6ce1cb0dd2d6812e4558041ebbbcfa54b000000001976a9144299ff317fcd12ef19047df66d72454691797bfc88acffffffff01983a0000000000001976a914b3e2819b6262e0b1f19fc7229d75677f347c91ac88ac0000000001000000

HASH256(message): a6b4103f527dfe43dfbadf530c247bac8a98b7463c7c6ad38eed97021d18ffcb
Ícone Ferramenta HASH256

HASH256

SHA-256 dupla. Usada para fazer o hash de cabeçalhos de bloco, dados de transação e de praticamente qualquer coisa que precise ser hasheada no Bitcoin.

0 bytes
SHA-256
SHA-256

SHA-256(SHA-256(dados))

0 bytes

Sempre assinamos hashes de dados. Hashear os dados cria uma impressão digital menor, mais eficiente de assinar.

HASH256(message) é uma abreviação de SHA-256(SHA-256(message)). Você quase sempre usa SHA-256 duplo ao hashear dados no Bitcoin.

5. Assinar o hash da transação

Agora podemos simplesmente assinar esse hash da mensagem exatamente como assinaríamos qualquer mensagem em ECDSA. Tudo o que precisamos é da nossa chave privada e de um nonce gerado aleatoriamente.

É assim que se parece a assinatura bruta desta transação:

nonce            (k): 75bcd15
hash256(message) (z): a6b4103f527dfe43dfbadf530c247bac8a98b7463c7c6ad38eed97021d18ffcb
private key      (d): f94a840f1e1a901843a75dd07ffcc5c84478dc4f987797474c9393ac53ab55e6

random point (k*G = R): {
  x = 4051293998585674784991639592782214972820158391371785981004352359465450369227,
  y = 88166831356626186178414913298033275054086243781277878360288998796587140930350
}

signature: r = R[x], s = k⁻¹ * (z + r * d): {
  r = 4051293998585674784991639592782214972820158391371785981004352359465450369227,
  s = 101656099268479774907861155236876278987061611115278341531512875302287938750185
}

A chave privada é a que foi usada para criar a chave pública a que a saída foi travada.

Você deve sempre usar um nonce único, gerado aleatoriamente, ao criar uma assinatura. Se você reutilizar o mesmo nonce com a mesma chave privada, suas moedas serão roubadas. Usei um nonce pequeno e fixo de 0x75bcd15 neste exemplo para que você possa recriar exatamente a mesma assinatura, se quiser.

Ícone Ferramenta Assinar com ECDSA

Assinatura ECDSA

Assine o hash de uma mensagem usando uma chave privada.

Normalmente é o hash de dados de uma transação (já preparados para assinatura)

0x
0 bytes
0x
0x
0 bytes
Assinatura
0d
0d
High: Low:

Nunca insira sua chave privada em um site, nem use uma chave privada gerada por um site. Sites podem facilmente salvar a chave privada e usá-la para roubar seus bitcoins.

6. Usar o valor s baixo

Curiosamente, em ECDSA existem na verdade dois valores possíveis de s que produzem uma assinatura válida. Chamamos um de valor s "alto" e o outro de valor s "baixo".

Em termos matemáticos, o "outro" valor s válido é apenas o inverso aditivo do nosso valor s atual no corpo finito de n. Mas não se preocupe com isso por agora.

Como resultado, ambos os valores s levam, na verdade, à mesma coordenada x do ponto aleatório R ao realizar a verificação da assinatura. Em termos simples, isso significa que existem basicamente duas versões diferentes da mesma assinatura.

Diagrama mostrando o valor s baixo e o valor s alto como ambos válidos durante a verificação da assinatura digital.

De qualquer forma, no Bitcoin esse recurso é um pouco chato, porque significa que qualquer pessoa poderia inverter o valor s da nossa assinatura depois de enviarmos a transação para a rede, e isso alteraria o TXID resultante da nossa transação assinada. Isso não muda a estrutura real da transação (o dinheiro vai para o mesmo lugar da mesma maneira), mas significa que perdemos a capacidade de rastrear a transação de forma confiável em um explorador de blockchain.

Então, para ajudar a remediar esse problema do valor s alto/baixo, o Bitcoin v0.10.3 e o Bitcoin v0.11.1 (outubro de 2015) introduziram uma regra de padronicidade em que todas as assinaturas devem usar o valor s baixo por padrão, caso contrário a transação é considerada não-padrão e não será retransmitida pelos nós (veja a BIP 62 para detalhes).

Você saberá que terminou com o valor s "alto" porque ele estará na metade superior do corpo finito de n (veja os parâmetros do ECDSA). Se for esse o caso (como com o nosso valor s), basta subtrair o valor s de n para obter o valor s "baixo":

n     = 115792089237316195423570985008687907852837564279074904382605163141518161494337

s     = 101656099268479774907861155236876278987061611115278341531512875302287938750185  <- valor s alto
n - s = 14135989968836420515709829771811628865775953163796562851092287839230222744152   <- valor s baixo

Como eu disse, ambos os valores s são tecnicamente válidos – é só que no Bitcoin usamos o valor s baixo para ajudar a evitar a maleabilidade da transação.

Este gráfico de valores S em assinaturas ECDSA mostra como a prevalência de valores s altos diminuiu ao longo do tempo. Tornou-se o padrão em março de 2014 (Bitcoin v0.9.0), antes de finalmente se tornar não-padrão em outubro de 2015 (Bitcoin v0.10.3 e Bitcoin v0.11.1).

7. Codificar a assinatura em DER

O próximo passo é formatar a assinatura pronta para ser colocada dentro de uma transação bitcoin.

O Bitcoin usa codificação DER para assinaturas, que é um pouco verbosa, mas foi o que Satoshi escolheu, então agora temos que usá-la ao codificar assinaturas antes de colocá-las dentro das transações.

Então, se esta é a nossa assinatura bruta:

signature: {
  r = 4051293998585674784991639592782214972820158391371785981004352359465450369227,
  s = 14135989968836420515709829771811628865775953163796562851092287839230222744152
}

É assim que ela se parece em codificação DER:

der encoded signature:
type: 30
  length: 44
  type:   02
    length: 20
    r:      08f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb
  type:   02
    length: 20
    s:      1f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a58

serialized: 3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a58
Ícone Ferramenta Assinatura DER

Codificação DER

Serializa os dois números de uma assinatura ECDSA (r e s) no formato DER, usado dentro das transações.

Assinatura
0d
0d
0 bytes

8. Anexar o tipo de hash da assinatura à assinatura codificada em DER

Depois de codificar a nossa assinatura no formato DER, anexamos novamente o tipo de hash da assinatura para indicar a quanto dos dados da transação esta assinatura se aplica.

Este deve ser o mesmo valor do tipo de hash da assinatura que escolhemos no passo 3, que foi SIGHASH_ALL (0x01).

Então é assim que se parece a nossa assinatura codificada agora:

der encoded signature (with SIGHASH):
type: 30
  length: 44
  type:   02
    length: 20
    r:      08f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb
  type:   02
    length: 20
    s:      1f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a58
sighash: 01

serialized:
3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a5801

O tipo SIGHASH aqui tem 1 byte, mesmo que o tipo SIGHASH que anexamos aos dados da transação antes de hashear tenha 4 bytes. Esta é só mais uma peculiaridade do bitcoin.

A razão de também termos anexado o tipo SIGHASH antes de hashear é que isso nos permite nos comprometer com esse tipo SIGHASH antes de criar a assinatura. Em outras palavras, se alguém alterar este tipo de sighash na assinatura para algo como SIGHASH_ANYONECANPAY (que sugere que qualquer um poderia adicionar mais entradas à transação), ele não vai corresponder ao valor SIGHASH que escolhemos quando hasheamos e assinamos os dados iniciais da transação, e assim a assinatura deixaria de ser válida.

9. Construir o ScriptSig

Diagrama mostrando a estrutura de um P2PKH.

Como passo final, precisamos colocar esta assinatura em um padrão de ScriptSig que destrave a entrada.

Não vou cobrir aqui a mecânica de como o Script funciona. Então, para simplificar, só vou lembrar você de que a nossa entrada escolhida tem o seguinte ScriptPubKey (script de travamento) P2PKH:

scriptpubkey:

asm: OP_DUP OP_HASH OP_PUSHBYTES_20 [public key hash] OP_EQUALVERIFY OP_CHECKSIG
hex: 76a9144299ff317fcd12ef19047df66d72454691797bfc88ac

Resumindo, esta entrada foi travada a um hash de chave pública. Para destravá-la, precisamos fornecer uma assinatura válida junto com a chave pública original.

Então, se esta é a nossa chave pública e assinatura:

public key: 024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1
signature:  3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a5801

É assim que se parece o nosso ScriptSig (script de desbloqueio) com a assinatura dentro dele:

scriptsig:

asm: OP_PUSHBYTES_71 [signature] OP_PUSHBYTES_33 [public key]
hex: 473044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a580121024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1

Isto é apenas a assinatura codificada em DER seguida da chave pública, com os bytes 0x47 e 0x21 antes de cada item para indicar o seu tamanho.

Ícone Ferramenta Script

Script

Decodifique e codifique um script.

0 bytes

10. Inserir o ScriptSig na transação

Por fim, basta colocarmos este ScriptSig nos dados da transação bruta com que começamos:

Transação Bruta (Assinada):

version: 01000000
inputs:  01
  txid: b7994a0db2f373a29227e1d90da883c6ce1cb0dd2d6812e4558041ebbbcfa54b
  vout: 00000000
  scriptsigsize: 6a
  scriptsig: 473044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a580121024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1
  sequence: ffffffff
outputs: 01
  amount: 983a000000000000
  scriptpubkeysize: 19
  scriptpubkey: 76a914b3e2819b6262e0b1f19fc7229d75677f347c91ac88ac
locktime: 00000000

Não se esqueça de atualizar o campo scriptsigsize.

Você precisará repetir os passos 1-9 para cada entrada que quiser destravar.

Por fim, se serializarmos esses dados da transação, obtemos:

Transação Bruta (Assinada):

0100000001b7994a0db2f373a29227e1d90da883c6ce1cb0dd2d6812e4558041ebbbcfa54b000000006a473044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a580121024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1ffffffff01983a0000000000001976a914b3e2819b6262e0b1f19fc7229d75677f347c91ac88ac00000000

E essa é uma transação válida que podemos enviar para a rede bitcoin.

Esta é uma transação real que eu mesmo construí para usar como exemplo. Você pode vê-la em um explorador de blockchain aqui: 1d5308ff12cb6fdb670c3af673a6a1317e21fa14fc863d5827f9d704cd5e14dc

Ícone Ferramenta TXID

TXID

Crie um TXID a partir dos dados de uma transação bruta.

0 bytes

Bytes da transação efetivamente usados no hash (sem marker, flag nem testemunha):

Usado internamente dentro dos dados da transação bruta

0 bytes

Usado externamente ao buscar transações em exploradores de blocos

0 bytes
bitcoin-cli sendrawtransaction

Use este comando para enviar uma transação bruta para a rede bitcoin. Por exemplo:

$ bitcoin-cli sendrawtransaction 0100000001b7994a0db2f373a29227e1d90da883c6ce1cb0dd2d6812e4558041ebbbcfa54b000000006a473044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a580121024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1ffffffff01983a0000000000001976a914b3e2819b6262e0b1f19fc7229d75677f347c91ac88ac00000000

Código

Aqui está um código Ruby que assina uma transação usando o algoritmo legado.

A primeira metade do código contém a matemática de curva elíptica necessária para criar assinaturas ECDSA, e a segunda metade contém o código para de fato assinar uma transação e colocar a assinatura no formato certo.

O código é básico, mas funciona.

require "digest" # para hashear os dados da transação e poder assiná-los
require "securerandom" # para gerar nonces aleatórios ao assinar

# -------------------------
# Parâmetros da Curva Elíptica
# -------------------------
# y² = x³ + ax + b
$a = 0
$b = 7

# corpo primo
$p = 2 ** 256 - 2 ** 32 - 2 ** 9 - 2 ** 8 - 2 ** 7 - 2 ** 6 - 2 ** 4 - 1

# número de pontos que conseguimos alcançar na curva ("ordem")
$n = 115792089237316195423570985008687907852837564279074904382605163141518161494337

# ponto gerador (o ponto inicial na curva usado em todos os cálculos)
$G = {
  x: 55066263022277343669578718895168534326250603453777594175500187360389116729240,
  y: 32670510020758816978083085130507043184471273380659243275938904335757337482424,
}

# ---------------
# Inverso Modular: Ruby não tem uma função modinv embutida
# ---------------
def inverse(a, m = $p)
  m_orig = m         # guarda o módulo original
  a = a % m if a < 0 # garante que a seja positivo
  y_prev = 0
  y = 1
  while a > 1
    q = m / a

    y_before = y # guarda o valor atual de y
    y = y_prev - q * y # calcula o novo valor de y
    y_prev = y_before # define o y anterior como o y antigo

    a_before = a # guarda o valor atual de a
    a = m % a # calcula o novo valor de a
    m = a_before # define m como o valor antigo de a
  end
  return y % m_orig
end

# ------
# Double: soma um ponto a si mesmo
# ------
def double(point)
  # slope = (3x₁² + a) / 2y₁
  slope = ((3 * point[:x] ** 2 + $a) * inverse((2 * point[:y]), $p)) % $p # usando inverse para ajudar na divisão

  # x = slope² - 2x₁
  x = (slope ** 2 - (2 * point[:x])) % $p

  # y = slope * (x₁ - x) - y₁
  y = (slope * (point[:x] - x) - point[:y]) % $p

  # Retorna o novo ponto
  return { x: x, y: y }
end

# ---
# Add: soma dois pontos
# ---
def add(point1, point2)
  # faz o double se os dois pontos forem iguais
  if point1 == point2
    return double(point1)
  end

  # slope = (y₁ - y₂) / (x₁ - x₂)
  slope = ((point1[:y] - point2[:y]) * inverse(point1[:x] - point2[:x], $p)) % $p

  # x = slope² - x₁ - x₂
  x = (slope ** 2 - point1[:x] - point2[:x]) % $p

  # y = slope * (x₁ - x) - y₁
  y = ((slope * (point1[:x] - x)) - point1[:y]) % $p

  # Retorna o novo ponto
  return { x: x, y: y }
end


# --------
# Multiply: usa as operações de double e add para multiplicar rapidamente um ponto por um valor inteiro (ou seja, uma chave privada)
# --------
def multiply(k, point = $G)
  # cria uma cópia do ponto inicial (para usar na soma adiante)
  current = point

  # converte o inteiro para representação binária
  binary = k.to_s(2)

  # algoritmo double-and-add para multiplicação rápida
  binary.split("").drop(1).each do |char| # da esquerda para a direita, ignorando o primeiro caractere binário
    # 0 = double
    current = double(current)

    # 1 = double e add
    current = add(current, point) if char == "1"
  end

  # retorna o ponto final
  current
end


# ----
# Sign
# ----
def sign(private_key, hash, nonce = nil)
  # gera um número aleatório se nenhum for fornecido
  if nonce == nil
    loop do
      nonce = SecureRandom.hex(32).to_i(16)
      break if nonce < $n # garante que o número aleatório seja menor que o número de pontos da curva
    end
  end

  # r = o valor x de um ponto aleatório na curva
  r = multiply(nonce)[:x] % $n

  # s = nonce⁻¹ * (hash + private_key * r) mod n
  s = (inverse(nonce, $n) * (hash + private_key * r)) % $n # isto quebra a linearidade (em comparação ao schnorr)

  # a assinatura é [r, s]
  return { r: r, s: s }
end


# ------------------
# Assinar uma Transação
# ------------------

# Uma estrutura básica para guardar os dados da transação
def tx(scriptsig)
  # Precisa calcular um byte indicando o tamanho do scriptsig que vem a seguir, em bytes (código tosco mas funciona)
  size = (scriptsig.length / 2).to_s(16).rjust(2, "0")

  # Dados da transação bruta não assinada com o campo scriptsig (você precisa saber a posição correta)
  return "0100000001b7994a0db2f373a29227e1d90da883c6ce1cb0dd2d6812e4558041ebbbcfa54b00000000#{size}#{scriptsig}ffffffff01983a0000000000001976a914b3e2819b6262e0b1f19fc7229d75677f347c91ac88ac00000000"
end

# Chave privada e chave pública dos bitcoins travados que queremos gastar
private_key = "f94a840f1e1a901843a75dd07ffcc5c84478dc4f987797474c9393ac53ab55e6" # sha256("learnmeabitcoin1")
public_key = "024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1"

# NOTA: Precisa remover primeiro todas as assinaturas existentes dos dados da transação, se houver alguma

# Coloca o scriptpubkey original como placeholder no scriptsig da entrada que você quer assinar (obrigatório)
scriptpubkey = "76a9144299ff317fcd12ef19047df66d72454691797bfc88ac" # apenas uma entrada nesta transação
transaction = tx(scriptpubkey)

# Anexa o tipo de sighash aos dados da transação (obrigatório)
transaction = transaction + "01000000"

# Obtém um hash dos dados da transação (porque assinamos o hash dos dados, e não os dados em si)
hash = Digest::SHA256.hexdigest(Digest::SHA256.digest([transaction].pack("H*")))

# Usa a matemática de curva elíptica para assinar o hash com a chave privada e o nonce
signature = sign(private_key.to_i(16), hash.to_i(16), 123456789) # usando um nonce fixo para teste (inseguro)

# Usa o valor s baixo (BIP 62: Lidando com a maleabilidade)
if (signature[:s] > $n / 2)
  signature[:s] = $n - signature[:s]
end

# Codifica a assinatura no formato DER (formato um pouco estranho usado para assinaturas em transações bitcoin)
r = signature[:r].to_s(16).rjust(64, "0")  # converte r para hexadecimal
s = signature[:s].to_s(16).rjust(64, "0")  # converte s para hexadecimal
r = "00" + r if (r[0, 2].to_i(16) >= 0x80) # prepende 00 se o primeiro byte for 0x80 ou maior (peculiaridade do DER)
s = "00" + r if (s[0, 2].to_i(16) >= 0x80) # prepende 00 se o primeiro byte for 0x80 ou maior (peculiaridade do DER)
der = ""                                   # string para guardar nossa codificação der
r_len = (r.length / 2).to_s(16).rjust(2, "0") # obtém o tamanho de r (em bytes)
s_len = (s.length / 2).to_s(16).rjust(2, "0") # obtém o tamanho de s (em bytes)
der << "02" << r_len << r << "02" << s_len << s   # Adiciona à codificação DER (o byte 0x02 indica um tipo inteiro no DER)
der_len = (der.length / 2).to_s(16).rjust(2, "0") # obtém o tamanho dos dados DER (em bytes)
der = "30" + der_len + der # Codificação DER final (o byte 0x30 indica um tipo de objeto composto)

# Anexa o tipo de sighash à assinatura (obrigatório) (01 = ALL)
der = der + "01" # sem ele você recebe "mandatory-script-verify-flag-failed (Non-canonical DER signature) (code 16)"

# Constrói o script de desbloqueio completo (scripts P2PKH precisam da chave pública original a que os bitcoins foram travados): <size> {signature} <size> {public_key}
scriptsig = (der.length / 2).to_s(16) + der + (public_key.length / 2).to_s(16) + public_key

# Coloca o scriptsig completo nos dados da transação original
transaction = tx(scriptsig)

# Mostra a transação assinada
puts transaction #=> 0100000001b7994a0db2f373a29227e1d90da883c6ce1cb0dd2d6812e4558041ebbbcfa54b000000006a473044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb02201f40afd1627798ee8529095ca4b205498032315240ac322c9d8ff0f205a93a580121024aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b1ffffffff01983a0000000000001976a914b3e2819b6262e0b1f19fc7229d75677f347c91ac88ac00000000

# Envia a transação para a rede bitcoin
# $ bitcoin-cli sendrawtransaction [dados da transação bruta]

Algoritmo SegWit

BIP 143

Este novo algoritmo de assinatura segwit foi introduzido na atualização Segregated Witness e é usado ao criar assinaturas para destravar o novo script de travamento P2WPKH.

Ele tem duas vantagens sobre o antigo "algoritmo legado":

  1. Mais eficiente. Os dados da transação são divididos em partes reutilizáveis separadas ao construir um hash da transação para assinatura. Isso acelera o processo de assinatura se houver várias entradas de transação para as quais você precise criar assinaturas.
  2. Exige o valor da entrada. Você precisa incluir o valor da entrada que está gastando ao criar cada assinatura. Isso força você a estar ciente do valor das entradas da transação, o que é útil para calcular a taxa da transação.

Os passos para criar uma assinatura são praticamente os mesmos do algoritmo legado. A única diferença está na forma como você prepara os dados da transação para assinatura.

Repeti vários dos mesmos passos acima apenas para poder mostrar um exemplo completo de como assinar e gastar uma saída P2WPKH.

0. Criar a transação

Ícone Ferramenta Construtor de Transação

Construtor de Transações

Monte os dados brutos de uma transação.

Tipo

Versão

0d
Transação Básica

Entradas (1)

Saídas (1)

Locktime

0d
Altura de Bloco

Dados Brutos da Transação

0 bytes 0 vbytes
Ícone Ferramenta Separador de Transação

Separador de Transações

Cole uma transação bruta (hexadecimal) para dividi-la nos seus campos individuais.

Comece criando uma transação bruta não assinada.

É assim que se parece a minha transação:

Transação Bruta (Não Assinada):

version: 02000000
marker: 00
flag: 01
inputs:  01
  txid: ac4994014aa36b7f53375658ef595b3cb2891e1735fe5b441686f5e53338e76a
  vout: 01000000
  scriptsigsize: 00
  scriptsig:
  sequence: ffffffff
outputs: 01
  amount: 204e000000000000
  scriptpubkeysize: 19
  scriptpubkey: 76a914ce72abfd0e6d9354a660c18f2825eb392f060fdc88ac
witness: 00
locktime: 00000000

Não se esqueça de que uma transação segwit (ou seja, uma transação com uma entrada P2WPKH ou P2WSH) deve incluir os novos campos de marcador, flag e testemunha.

Nesta transação estou gastando uma saída P2WPKH (no valor de 30.000 sats) de uma transação anterior como minha entrada. Depois estou "enviando-a" para uma nova saída P2PKH (no valor de 20.000 sats).

Como você pode ver, o campo de testemunha está vazio no momento. É aqui que a minha assinatura será eventualmente colocada.

O campo ScriptSig não será usado, pois as entradas P2WPKH usam o campo de testemunha para o código de desbloqueio, em vez do campo ScriptSig legado.

1. Construir o preimage e o hash do preimage

É aqui que o algoritmo de assinatura segwit difere do algoritmo de assinatura legado.

Em vez de hashear os dados da transação como estão, nós os dividimos em partes individuais e depois as reconstruímos de uma forma específica para criar um novo pedaço de dados que pode ser hasheado para assinatura.

A razão para isso é que muitas partes dos dados da transação não assinada podem ser reutilizadas mais tarde, se a transação tiver muitas entradas para as quais precisamos criar assinaturas.

1. Pegue o campo version (reutilizável)

Comece pegando o campo version da transação bruta não assinada.

version = 02000000
2. Serialize e faça o hash dos TXIDs e VOUTs das entradas (reutilizável)

Em seguida, serializamos todos os TXID+VOUTs de todas as entradas da transação e depois fazemos o hash deles usando HASH256.

inputs          = ac4994014aa36b7f53375658ef595b3cb2891e1735fe5b441686f5e53338e76a01000000
hash256(inputs) = cbfaca386d65ea7043aaac40302325d0dc7391a73b585571e28d3287d6b16203
Ícone Ferramenta HASH256

HASH256

SHA-256 dupla. Usada para fazer o hash de cabeçalhos de bloco, dados de transação e de praticamente qualquer coisa que precise ser hasheada no Bitcoin.

0 bytes
SHA-256
SHA-256

SHA-256(SHA-256(dados))

0 bytes

Minha transação tem apenas uma entrada, então o resultado é só hash256(TXID+VOUT).

Se eu tivesse mais entradas, o resultado seria hash256((TXID+VOUT)+(TXID+VOUT)+(TXID+VOUT)...).

3. Serialize e faça o hash das sequências das entradas (reutilizável)

Isto é igual ao passo anterior, exceto que serializamos e fazemos o hash das sequências das entradas separadamente:

sequences          = ffffffff
hash256(sequences) = 3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044
Ícone Ferramenta HASH256

HASH256

SHA-256 dupla. Usada para fazer o hash de cabeçalhos de bloco, dados de transação e de praticamente qualquer coisa que precise ser hasheada no Bitcoin.

0 bytes
SHA-256
SHA-256

SHA-256(SHA-256(dados))

0 bytes
4. Serialize o TXID e o VOUT da entrada que estamos assinando (não reutilizável)

Em seguida, serializamos o TXID+VOUT da entrada específica para a qual queremos criar uma assinatura.

Minha transação tem apenas uma entrada, então este é o mesmo valor do TXID+VOUT serializado no passo 2.

input = ac4994014aa36b7f53375658ef595b3cb2891e1735fe5b441686f5e53338e76a01000000
5. Crie um scriptcode para a entrada que estamos assinando (não reutilizável)
Diagrama mostrando como converter um ScriptPubKey P2WPKH em um ScriptCode.

O "scriptcode" é uma versão modificada do ScriptPubKey da saída que estamos gastando.

Para criar o scriptcode, você precisa encontrar o ScriptPubKey da saída que quer gastar, extrair o hash da chave pública e colocá-lo na seguinte estrutura de ScriptPubKey P2PKH:

scriptcode = 1976a914{publickeyhash}88ac

Se olharmos a transação anterior, vemos que o ScriptPubKey da saída P2WPKH que estou gastando é:

scriptpubkey = 0014aa966f56de599b4094b61aa68a2b3df9e97e9c48

Então o scriptcode é:

scriptcode = 1976a914aa966f56de599b4094b61aa68a2b3df9e97e9c4888ac

Como você pode ver, simplesmente extraí o hash da chave pública (aa966f56de599b4094b61aa68a2b3df9e97e9c48) do script de travamento original e o coloquei dentro de um script de travamento P2PKH.

Não tenho total certeza de por que fazemos isso. Meu único palpite é que isso faz o scriptcode ficar parecido com o placeholder usado no passo 2 do algoritmo legado.

6. Encontre o valor da entrada (não reutilizável)

Em seguida, descobrimos o valor da entrada. Se olharmos a transação anterior, a saída tem um valor de 30000 satoshis.

Se convertermos 30000 para um campo little-endian de 8 bytes (a mesma estrutura do campo amount em uma transação bruta), obtemos:

amount = 3075000000000000
Ícone Ferramenta Little Endian

Little Endian

Converta um número entre as ordens de bytes big-endian e little-endian.

0d
0x
0 bytes
0x
0 bytes
Tamanho do Campo
7. Pegue a sequência da entrada que estamos assinando (não reutilizável)

Este é o campo de sequência da entrada para a qual estamos criando a assinatura.

Minha transação tem apenas uma entrada, então isto é igual às sequências do passo 3.

sequence = ffffffff
8. Serialize e faça o hash de todas as saídas (reutilizável)

Neste passo serializamos todos os dados das saídas da transação e depois fazemos o hash deles usando HASH256.

outputs          = 204e0000000000001976a914ce72abfd0e6d9354a660c18f2825eb392f060fdc88ac
hash256(outputs) = 900a6c6ff6cd938bf863e50613a4ed5fb1661b78649fe354116edaf5d4abb952
Ícone Ferramenta HASH256

HASH256

SHA-256 dupla. Usada para fazer o hash de cabeçalhos de bloco, dados de transação e de praticamente qualquer coisa que precise ser hasheada no Bitcoin.

0 bytes
SHA-256
SHA-256

SHA-256(SHA-256(dados))

0 bytes

Minha transação tem apenas uma saída, então o resultado é só hash256(amount+scriptpubkeysize+scriptpubkey).

Se eu tivesse mais saídas, serializaria os dados de todas as saídas uma após a outra (similar a como serializamos as entradas no passo 2).

9. Pegue o locktime (reutilizável)

Este é apenas o campo locktime da nossa transação bruta não assinada.

locktime = 00000000
10. Combine para criar um preimage do hash

Agora concatenamos todas essas partes dos passos acima para criar um "preimage" do hash.

Isto é basicamente uma reconstrução de todos os dados da transação bruta que queremos assinar a partir dos passos acima:

preimage = version + hash256(inputs) + hash256(sequences) + input + scriptcode + amount + sequence + hash256(outputs) + locktime

preimage = 02000000cbfaca386d65ea7043aaac40302325d0dc7391a73b585571e28d3287d6b162033bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044ac4994014aa36b7f53375658ef595b3cb2891e1735fe5b441686f5e53338e76a010000001976a914aa966f56de599b4094b61aa68a2b3df9e97e9c4888ac3075000000000000ffffffff900a6c6ff6cd938bf863e50613a4ed5fb1661b78649fe354116edaf5d4abb95200000000
11. Adicione o tipo de hash da assinatura ao fim do preimage do hash

Agora adicionamos um tipo de hash da assinatura ao fim deste preimage para indicar a quanto dos dados da transação estamos assinando.

Neste exemplo quero criar uma assinatura que se aplica à transação inteira, então estou usando SIGHASH_ALL (0x01).

preimage = 02000000cbfaca386d65ea7043aaac40302325d0dc7391a73b585571e28d3287d6b162033bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044ac4994014aa36b7f53375658ef595b3cb2891e1735fe5b441686f5e53338e76a010000001976a914aa966f56de599b4094b61aa68a2b3df9e97e9c4888ac3075000000000000ffffffff900a6c6ff6cd938bf863e50613a4ed5fb1661b78649fe354116edaf5d4abb9520000000001000000

O tipo de hash da assinatura é um campo little-endian de 4 bytes quando o adicionamos ao preimage do hash dos dados da transação.

12. Faça o hash do preimage

Por fim, fazemos o hash deste preimage usando HASH256 para criar a mensagem final que vamos assinar:

message = hash256(preimage)
message = d7b60220e1b9b2c1ab40845118baf515203f7b6f0ad83cbb68d3c89b5b3098a6
Ícone Ferramenta HASH256

HASH256

SHA-256 dupla. Usada para fazer o hash de cabeçalhos de bloco, dados de transação e de praticamente qualquer coisa que precise ser hasheada no Bitcoin.

0 bytes
SHA-256
SHA-256

SHA-256(SHA-256(dados))

0 bytes

2. Assinar o hash do preimage

Usando o hash do preimage que criamos no passo anterior, nós o assinamos usando a chave privada e um nonce gerado aleatoriamente:

nonce             (k): 75bcd15 (hex)
hash256(preimage) (z): d7b60220e1b9b2c1ab40845118baf515203f7b6f0ad83cbb68d3c89b5b3098a6 (hex)
private key       (d): 7306f5092467981e66eff98b6b03bfe925922c5ecfaf14c4257ef18e81becf1f (hex)

random point (k*G = R): {
  x = 4051293998585674784991639592782214972820158391371785981004352359465450369227,
  y = 88166831356626186178414913298033275054086243781277878360288998796587140930350
}

signature: r = R[x], s = k⁻¹ * (z + r * d): {
  r = 4051293998585674784991639592782214972820158391371785981004352359465450369227,
  s = 22928756034338380041288899807245402174768928418361705349511346173579327129676
}

Você deve sempre usar um nonce único, gerado aleatoriamente, ao criar uma assinatura. Se você reutilizar o mesmo nonce com a mesma chave privada, suas moedas serão roubadas. Usei um nonce pequeno e fixo de 0x75bcd15 neste exemplo para que você possa recriar exatamente a mesma assinatura, se quiser.

Ícone Ferramenta Conversor de Números

Conversor de Números

Converta um número entre bases diferentes (binário, decimal, hexadecimal).

0b
0 dígitos
0d
0 dígitos
0x
0 dígitos
Ícone Ferramenta Assinar com ECDSA

Assinatura ECDSA

Assine o hash de uma mensagem usando uma chave privada.

Normalmente é o hash de dados de uma transação (já preparados para assinatura)

0x
0 bytes
0x
0x
0 bytes
Assinatura
0d
0d
High: Low:

Nunca insira sua chave privada em um site, nem use uma chave privada gerada por um site. Sites podem facilmente salvar a chave privada e usá-la para roubar seus bitcoins.

3. Usar o valor s baixo

No Bitcoin agora sempre usamos o valor s baixo da assinatura. Se usássemos o valor s alto, a transação seria considerada não-padrão e não seria retransmitida pelos nós da rede.

Nesta transação de exemplo, o valor s já era o valor baixo, então não preciso convertê-lo.

s (high) = 92863333202977815382282085201442505678068635860713199033093816967938834364661
s (low)  = 22928756034338380041288899807245402174768928418361705349511346173579327129676

4. Codificar a assinatura em DER

Em seguida convertemos os valores r e s da assinatura em codificação DER.

signature: {
  r = 4051293998585674784991639592782214972820158391371785981004352359465450369227,
  s = 22928756034338380041288899807245402174768928418361705349511346173579327129676
}

der encoded signature:
type: 30
  length: 44
  type:   02
    length: 20
    r:      08f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb
  type:   02
    length: 20
    s:      32b1374d1a0f125eae4f69d1bc0b7f896c964cfdba329f38a952426cf427484c

serialized: 3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb022032b1374d1a0f125eae4f69d1bc0b7f896c964cfdba329f38a952426cf427484c
Ícone Ferramenta Assinatura DER

Codificação DER

Serializa os dois números de uma assinatura ECDSA (r e s) no formato DER, usado dentro das transações.

Assinatura
0d
0d
0 bytes

5. Anexar o tipo de hash da assinatura à assinatura codificada em DER

Não se esqueça de adicionar o tipo de hash da assinatura à assinatura. Ele deve ser o mesmo valor que anexamos ao hash do preimage no passo 1.

O tipo de hash da assinatura que estou usando é SIGHASH_ALL (0x01).

der encoded signature (with sighash): 3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb022032b1374d1a0f125eae4f69d1bc0b7f896c964cfdba329f38a952426cf427484c01

O tipo de hash da assinatura anexado a uma assinatura codificada em DER tem 1 byte de tamanho (ao contrário do tipo de sighash little-endian de 4 bytes do passo 1).

6. Construir o campo de testemunha

Agora que temos a nossa assinatura completa, precisamos construir o código de desbloqueio completo que destravará a entrada.

A saída que estou gastando tinha um ScriptPubKey (script de travamento) P2WPKH. Então, para destravá-la, preciso construir um campo de testemunha contendo a assinatura e a chave pública original.

Não vou cobrir aqui os detalhes da estrutura de um campo de testemunha, então vou apenas mostrar como ele se parece:

signature:  3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb022032b1374d1a0f125eae4f69d1bc0b7f896c964cfdba329f38a952426cf427484c01
public key: 03eed0d937090cae6ffde917de8a80dc6156e30b13edd5e51e2e50d52428da1c87

witness = 02473044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb022032b1374d1a0f125eae4f69d1bc0b7f896c964cfdba329f38a952426cf427484c012103eed0d937090cae6ffde917de8a80dc6156e30b13edd5e51e2e50d52428da1c87

7. Inserir o campo de testemunha na transação

Por fim, inserimos este campo de testemunha completo na transação:

version: 02000000
marker: 00
flag: 01
inputs:  01
  txid: ac4994014aa36b7f53375658ef595b3cb2891e1735fe5b441686f5e53338e76a
  vout: 01000000
  scriptsigsize: 00
  scriptsig:
  sequence: ffffffff
outputs: 01
  amount: 204e000000000000
  scriptpubkeysize: 19
  scriptpubkey: 76a914ce72abfd0e6d9354a660c18f2825eb392f060fdc88ac
witness: 02473044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb022032b1374d1a0f125eae4f69d1bc0b7f896c964cfdba329f38a952426cf427484c012103eed0d937090cae6ffde917de8a80dc6156e30b13edd5e51e2e50d52428da1c87
locktime: 00000000

Se serializarmos esses dados da transação, obtemos:

02000000000101ac4994014aa36b7f53375658ef595b3cb2891e1735fe5b441686f5e53338e76a0100000000ffffffff01204e0000000000001976a914ce72abfd0e6d9354a660c18f2825eb392f060fdc88ac02473044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb022032b1374d1a0f125eae4f69d1bc0b7f896c964cfdba329f38a952426cf427484c012103eed0d937090cae6ffde917de8a80dc6156e30b13edd5e51e2e50d52428da1c8700000000

E essa é a nossa transação assinada e pronta para ser enviada para a rede bitcoin.

Esta é uma transação real que eu fiz. Você pode vê-la em um explorador de blockchain aqui: 04f7bc0296fe70799762e628445fa9f0ccc2a2646ee5b369047d86ff964bd74e

Ícone Ferramenta TXID

TXID

Crie um TXID a partir dos dados de uma transação bruta.

0 bytes

Bytes da transação efetivamente usados no hash (sem marker, flag nem testemunha):

Usado internamente dentro dos dados da transação bruta

0 bytes

Usado externamente ao buscar transações em exploradores de blocos

0 bytes
bitcoin-cli sendrawtransaction

Use este comando para enviar uma transação bruta para a rede bitcoin. Por exemplo:

$ bitcoin-cli sendrawtransaction 02000000000101ac4994014aa36b7f53375658ef595b3cb2891e1735fe5b441686f5e53338e76a0100000000ffffffff01204e0000000000001976a914ce72abfd0e6d9354a660c18f2825eb392f060fdc88ac02473044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb022032b1374d1a0f125eae4f69d1bc0b7f896c964cfdba329f38a952426cf427484c012103eed0d937090cae6ffde917de8a80dc6156e30b13edd5e51e2e50d52428da1c8700000000

Código

Aqui está um código Ruby que assina uma transação usando o algoritmo segwit.

Novamente, este código está aqui apenas para mostrar os passos básicos envolvidos na criação de uma assinatura para um P2WPKH.

require "digest" # para hashear os dados da transação e poder assiná-los
require "securerandom" # para gerar nonces aleatórios ao assinar

# -------------------------
# Parâmetros da Curva Elíptica
# -------------------------
# y² = x³ + ax + b
$a = 0
$b = 7

# corpo primo
$p = 2 ** 256 - 2 ** 32 - 2 ** 9 - 2 ** 8 - 2 ** 7 - 2 ** 6 - 2 ** 4 - 1

# número de pontos que conseguimos alcançar na curva ("ordem")
$n = 115792089237316195423570985008687907852837564279074904382605163141518161494337

# ponto gerador (o ponto inicial na curva usado em todos os cálculos)
$G = {
  x: 55066263022277343669578718895168534326250603453777594175500187360389116729240,
  y: 32670510020758816978083085130507043184471273380659243275938904335757337482424,
}

# ---------------
# Inverso Modular: Ruby não tem uma função modinv embutida
# ---------------
def inverse(a, m = $p)
  m_orig = m         # guarda o módulo original
  a = a % m if a < 0 # garante que a seja positivo
  y_prev = 0
  y = 1
  while a > 1
    q = m / a

    y_before = y # guarda o valor atual de y
    y = y_prev - q * y # calcula o novo valor de y
    y_prev = y_before # define o y anterior como o y antigo

    a_before = a # guarda o valor atual de a
    a = m % a # calcula o novo valor de a
    m = a_before # define m como o valor antigo de a
  end
  return y % m_orig
end

# ------
# Double: soma um ponto a si mesmo
# ------
def double(point)
  # slope = (3x₁² + a) / 2y₁
  slope = ((3 * point[:x] ** 2 + $a) * inverse((2 * point[:y]), $p)) % $p # usando inverse para ajudar na divisão

  # x = slope² - 2x₁
  x = (slope ** 2 - (2 * point[:x])) % $p

  # y = slope * (x₁ - x) - y₁
  y = (slope * (point[:x] - x) - point[:y]) % $p

  # Retorna o novo ponto
  return { x: x, y: y }
end

# ---
# Add: soma dois pontos
# ---
def add(point1, point2)
  # faz o double se os dois pontos forem iguais
  if point1 == point2
    return double(point1)
  end

  # slope = (y₁ - y₂) / (x₁ - x₂)
  slope = ((point1[:y] - point2[:y]) * inverse(point1[:x] - point2[:x], $p)) % $p

  # x = slope² - x₁ - x₂
  x = (slope ** 2 - point1[:x] - point2[:x]) % $p

  # y = slope * (x₁ - x) - y₁
  y = ((slope * (point1[:x] - x)) - point1[:y]) % $p

  # Retorna o novo ponto
  return { x: x, y: y }
end


# --------
# Multiply: usa as operações de double e add para multiplicar rapidamente um ponto por um valor inteiro (ou seja, uma chave privada)
# --------
def multiply(k, point = $G)
  # cria uma cópia do ponto inicial (para usar na soma adiante)
  current = point

  # converte o inteiro para representação binária
  binary = k.to_s(2)

  # algoritmo double-and-add para multiplicação rápida
  binary.split("").drop(1).each do |char| # da esquerda para a direita, ignorando o primeiro caractere binário
    # 0 = double
    current = double(current)

    # 1 = double e add
    current = add(current, point) if char == "1"
  end

  # retorna o ponto final
  current
end


# ----
# Sign
# ----
def sign(private_key, hash, nonce = nil)
  # gera um número aleatório se nenhum for fornecido
  if nonce == nil
    loop do
      nonce = SecureRandom.hex(32).to_i(16)
      break if nonce < $n # garante que o número aleatório seja menor que o número de pontos da curva
    end
  end

  # r = o valor x de um ponto aleatório na curva
  r = multiply(nonce)[:x] % $n

  # s = nonce⁻¹ * (hash + private_key * r) mod n
  s = (inverse(nonce, $n) * (hash + private_key * r)) % $n # isto quebra a linearidade (em comparação ao schnorr)

  # a assinatura é [r, s]
  return { r: r, s: s }
end


# ------------------
# Assinar uma Transação
# ------------------

# Uma estrutura básica para guardar os dados brutos da transação
# É mais fácil usar uma estrutura flexível para podermos dividi-la em partes para o algoritmo de assinatura do BIP143
tx = {
  :version => "02000000",
  :marker => "00",
  :flag => "01",
  :inputcount => "01",
  :inputs => {
    0 => {
      :txid => "ac4994014aa36b7f53375658ef595b3cb2891e1735fe5b441686f5e53338e76a",
      :vout => "01000000",
      :scriptsigsize => "00",
      :scriptsig => "",
      :sequence => "ffffffff",
    }
  },
  :outputcount => "01",
  :outputs => {
    0 => {
      :amount => "204e000000000000",
      :scriptpubkeysize => "19",
      :scriptpubkey => "76a914ce72abfd0e6d9354a660c18f2825eb392f060fdc88ac",
    }
  },
  :witness => "00",
  :locktime => "00000000",
}

# Chave privada e chave pública dos bitcoins travados que queremos gastar
private_key = "7306f5092467981e66eff98b6b03bfe925922c5ecfaf14c4257ef18e81becf1f"
public_key = "03eed0d937090cae6ffde917de8a80dc6156e30b13edd5e51e2e50d52428da1c87"

# O valor da entrada que estamos gastando, em satoshis (precisa descobrir isto antes; obrigatório para o algoritmo de assinatura do BIP 143)
input_value = 30000

# Prepara as partes dos dados da transação bruta não assinada para construir o preimage do hash

# 1. Serializa e faz o hash dos txids e vouts das entradas
prevouts = ""
tx[:inputs].each do |i, input|
  prevouts << "#{input[:txid]}#{input[:vout]}"
end
prevouts_hash = Digest::SHA256.hexdigest(Digest::SHA256.digest([prevouts].pack("H*")))

# 2. Serializa e faz o hash das sequências das entradas
sequences = ""
tx[:inputs].each do |i, input|
  sequences << input[:sequence]
end
sequences_hash = Digest::SHA256.hexdigest(Digest::SHA256.digest([sequences].pack("H*")))

# 3. Serializa e faz o hash das saídas
outputs = ""
tx[:outputs].each do |i, output|
  outputs << "#{output[:amount]}#{output[:scriptpubkeysize]}#{output[:scriptpubkey]}"
end
outputs_hash = Digest::SHA256.hexdigest(Digest::SHA256.digest([outputs].pack("H*")))

# 4. Serializa a entrada específica que estamos assinando
input = tx[:inputs][0][:txid] + tx[:inputs][0][:vout]

# 5. Constrói o scriptcode para a entrada que estamos assinando (o hash da chave pública dentro de um script P2PKH equivalente)
scriptcode = "1976a914#{Digest::RMD160.hexdigest(Digest::SHA256.digest([public_key].pack("H*")))}88ac"

# 6. Serializa o valor da entrada (precisa saber isto antes)
amount = input_value.to_i.to_s(16).rjust(16, '0').to_s.scan(/../).reverse.join # hexadecimal, 8 bytes, little-endian

# 7. Obtém a sequência da entrada que estamos assinando
sequence = tx[:inputs][0][:sequence]

# Constrói o preimage do hash
preimage = "";
preimage << tx[:version]; # reutilizável
preimage << prevouts_hash; # reutilizável
preimage << sequences_hash; # reutilizável
preimage << input;
preimage << scriptcode;
preimage << amount;
preimage << sequence;
preimage << outputs_hash; # reutilizável
preimage << tx[:locktime]; # reutilizável

# Adiciona o tipo de sighash ao preimage (01 = SIGHASH_ALL)
preimage = preimage + "01000000"

# Faz o hash do preimage para criar a mensagem que vamos assinar
hash = Digest::SHA256.hexdigest(Digest::SHA256.digest([preimage].pack("H*")))

# Usa a matemática de curva elíptica para assinar o hash com a chave privada e o nonce
signature = sign(private_key.to_i(16), hash.to_i(16), 123456789) # usando um nonce fixo para teste (inseguro)

# Usa o valor s baixo (BIP 62: Lidando com a maleabilidade)
if (signature[:s] > $n / 2)
  signature[:s] = $n - signature[:s]
end

# Codifica a assinatura no formato DER (formato um pouco estranho usado para assinaturas em transações bitcoin)
r = signature[:r].to_s(16).rjust(64, "0")  # converte r para hexadecimal
s = signature[:s].to_s(16).rjust(64, "0")  # converte s para hexadecimal
r = "00" + r if (r[0, 2].to_i(16) >= 0x80) # prepende 00 se o primeiro byte for 0x80 ou maior (peculiaridade do DER)
s = "00" + r if (s[0, 2].to_i(16) >= 0x80) # prepende 00 se o primeiro byte for 0x80 ou maior (peculiaridade do DER)
der = ""                                   # string para guardar nossa codificação der
r_len = (r.length / 2).to_s(16).rjust(2, "0") # obtém o tamanho de r (em bytes)
s_len = (s.length / 2).to_s(16).rjust(2, "0") # obtém o tamanho de s (em bytes)
der << "02" << r_len << r << "02" << s_len << s   # Adiciona à codificação DER (o byte 0x02 indica um tipo inteiro no DER)
der_len = (der.length / 2).to_s(16).rjust(2, "0") # obtém o tamanho dos dados DER (em bytes)
der = "30" + der_len + der # Codificação DER final (o byte 0x30 indica um tipo de objeto composto)

# Anexa o tipo de sighash à assinatura (obrigatório) (01 = SIGHASH_ALL)
der_signature = der + "01" # sem ele você recebe "mandatory-script-verify-flag-failed (Non-canonical DER signature) (code 16)"

# Calcula o tamanho da assinatura (terá 0x47 ou 0x48 bytes de comprimento)
der_signature_len = (der_signature.length / 2).to_s(16).rjust(2, "0") # tecnicamente um campo "compact size", mas um único byte aqui é suficiente para o tamanho das assinaturas

# Constrói o script de desbloqueio completo (este é um campo witness contendo 2 itens: uma assinatura seguida de uma chave pública)
witness = "02#{der_signature_len}#{der_signature}21#{public_key}"

# Coloca o witness nos dados da transação original
tx[:witness] = witness

# Serializa os dados brutos da transação assinada para podermos enviá-la à rede
transaction = ""
transaction << tx[:version]
transaction << tx[:marker]
transaction << tx[:flag]
transaction << tx[:inputcount]
transaction << tx[:inputs][0][:txid]
transaction << tx[:inputs][0][:vout]
transaction << tx[:inputs][0][:scriptsigsize]
transaction << tx[:inputs][0][:scriptsig]
transaction << tx[:inputs][0][:sequence]
transaction << tx[:outputcount]
transaction << tx[:outputs][0][:amount]
transaction << tx[:outputs][0][:scriptpubkeysize]
transaction << tx[:outputs][0][:scriptpubkey]
transaction << tx[:witness]
transaction << tx[:locktime]

# Mostra a transação assinada
puts transaction #=> 02000000000101ac4994014aa36b7f53375658ef595b3cb2891e1735fe5b441686f5e53338e76a0100000000ffffffff01204e0000000000001976a914ce72abfd0e6d9354a660c18f2825eb392f060fdc88ac02473044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb022032b1374d1a0f125eae4f69d1bc0b7f896c964cfdba329f38a952426cf427484c012103eed0d937090cae6ffde917de8a80dc6156e30b13edd5e51e2e50d52428da1c8700000000

# Envia a transação para a rede bitcoin
# $ bitcoin-cli sendrawtransaction [dados da transação bruta]

Algoritmo Taproot

BIP 341

Veja o algoritmo de assinatura do taproot para detalhes.

O algoritmo de assinatura taproot foi introduzido na atualização Taproot e é usado ao criar assinaturas para destravar o novo script de travamento P2TR.

Ele é similar ao algoritmo segwit, mas tem alguns ajustes menores na construção do preimage e usa o esquema de assinatura Schnorr em vez de ECDSA.

O algoritmo de assinatura taproot inclui muitos dados adicionais específicos de P2TR e varia dependendo do método que você está usando para gastar. Acho mais fácil seguir os passos para criar assinaturas de saídas P2TR na própria página do Taproot, em vez de tentar descrevê-los aqui.

Localização

Onde você encontra assinaturas no Bitcoin?

As assinaturas podem ser encontradas dentro das áreas de ScriptSig e/ou Testemunha de uma transação.

Em outras palavras, elas ficam dentro dos campos usados para destravar entradas.

A localização exata de cada assinatura depende do tipo de script de travamento que foi colocado em uma saída anterior. Aqui estão alguns exemplos comuns de onde você encontrará assinaturas em transações brutas:

P2PK

Ao destravar um P2PK, uma única assinatura pode ser encontrada como o único data push no ScriptSig:

Bruto

01000000019d7a3553c3faec3d88d18b36ec3bfcdf00c7639ea161205a02e7fc9a1a25b61d0100000049483045022100c219a522e65ca8500ebe05a70d5a49d840ccc15f2afa4ee9df783f06b2a322310220489a46c37feb33f52c586da25c70113b8eea41216440eb84771cb67a67fdb68c01ffffffff0200f2052a010000001976a914e32acf8e6718a32029dc395cca1e0ac45c33f14188ac00c817a8040000004341049464205950188c29d377eebca6535e0f3699ce4069ecd77ffebfbd0bcf95e3c134cb7d2742d800a12df41413a09ef87a80516353a2f0a280547bb5512dc03da8ac00000000

Separado

{
  "version": "01000000",
  "inputcount": "01",
  "inputs": [
    {
      "txid": "9d7a3553c3faec3d88d18b36ec3bfcdf00c7639ea161205a02e7fc9a1a25b61d",
      "vout": "01000000",
      "scriptsigsize": "49",
      "scriptsig": "483045022100c219a522e65ca8500ebe05a70d5a49d840ccc15f2afa4ee9df783f06b2a322310220489a46c37feb33f52c586da25c70113b8eea41216440eb84771cb67a67fdb68c01",
      "sequence": "ffffffff"
    }
  ],
  "outputcount": "02",
  "outputs": [
    {
      "amount": "00f2052a01000000",
      "scriptpubkeysize": "19",
      "scriptpubkey": "76a914e32acf8e6718a32029dc395cca1e0ac45c33f14188ac"
    },
    {
      "amount": "00c817a804000000",
      "scriptpubkeysize": "43",
      "scriptpubkey": "41049464205950188c29d377eebca6535e0f3699ce4069ecd77ffebfbd0bcf95e3c134cb7d2742d800a12df41413a09ef87a80516353a2f0a280547bb5512dc03da8ac"
    }
  ],
  "locktime": "00000000"
}

Transação: e827a366ad4fc9a305e0901fe1eefc7e9fb8d70655a079877cf1ead0c3618ec0

P2PKH

Ao destravar um P2PKH, uma única assinatura é encontrada junto à chave pública como o primeiro data push no ScriptSig:

Bruto

0100000001a4e61ed60e66af9f7ca4f2eb25234f6e32e0cb8f6099db21a2462c42de61640b010000006b483045022100c233c3a8a510e03ad18b0a24694ef00c78101bfd5ac075b8c1037952ce26e91e02205aa5f8f88f29bb4ad5808ebc12abfd26bd791256f367b04c6d955f01f28a7724012103f0609c81a45f8cab67fc2d050c21b1acd3d37c7acfd54041be6601ab4cef4f31feffffff02f9243751130000001976a9140c443537e6e31f06e6edb2d4bb80f8481e2831ac88ac14206c00000000001976a914d807ded709af8893f02cdc30a37994429fa248ca88ac751a0600

Separado

{
  "version": "01000000",
  "inputcount": "01",
  "inputs": [
    {
      "txid": "a4e61ed60e66af9f7ca4f2eb25234f6e32e0cb8f6099db21a2462c42de61640b",
      "vout": "01000000",
      "scriptsigsize": "6b",
      "scriptsig": "483045022100c233c3a8a510e03ad18b0a24694ef00c78101bfd5ac075b8c1037952ce26e91e02205aa5f8f88f29bb4ad5808ebc12abfd26bd791256f367b04c6d955f01f28a7724012103f0609c81a45f8cab67fc2d050c21b1acd3d37c7acfd54041be6601ab4cef4f31",
      "sequence": "feffffff"
    }
  ],
  "outputcount": "02",
  "outputs": [
    {
      "amount": "f924375113000000",
      "scriptpubkeysize": "19",
      "scriptpubkey": "76a9140c443537e6e31f06e6edb2d4bb80f8481e2831ac88ac"
    },
    {
      "amount": "14206c0000000000",
      "scriptpubkeysize": "19",
      "scriptpubkey": "76a914d807ded709af8893f02cdc30a37994429fa248ca88ac"
    }
  ],
  "locktime": "751a0600"
}

Transação: 40e331b67c0fe7750bb3b1943b378bf702dce86124dc12fa5980f975db7ec930

P2MS

Ao destravar um P2MS, haverá uma ou mais assinaturas no ScriptSig.

Esta transação em particular está destravando um script de travamento P2MS 2-de-3, então o ScriptSig contém duas assinaturas:

Bruto

010000000110a5fee9786a9d2d72c25525e52dd70cbd9035d5152fac83b62d3aa7e2301d58000000009300483045022100af204ef91b8dba5884df50f87219ccef22014c21dd05aa44470d4ed800b7f6e40220428fe058684db1bb2bfb6061bff67048592c574effc217f0d150daedcf36787601483045022100e8547aa2c2a2761a5a28806d3ae0d1bbf0aeff782f9081dfea67b86cacb321340220771a166929469c34959daf726a2ac0c253f9aff391e58a3c7cb46d8b7e0fdc4801ffffffff0180a21900000000001976a914971802edf585cdbc4e57017d6e5142515c1e502888ac00000000

Separado

{
  "version": "01000000",
  "inputcount": "01",
  "inputs": [
    {
      "txid": "10a5fee9786a9d2d72c25525e52dd70cbd9035d5152fac83b62d3aa7e2301d58",
      "vout": "00000000",
      "scriptsigsize": "93",
      "scriptsig": "00483045022100af204ef91b8dba5884df50f87219ccef22014c21dd05aa44470d4ed800b7f6e40220428fe058684db1bb2bfb6061bff67048592c574effc217f0d150daedcf36787601483045022100e8547aa2c2a2761a5a28806d3ae0d1bbf0aeff782f9081dfea67b86cacb321340220771a166929469c34959daf726a2ac0c253f9aff391e58a3c7cb46d8b7e0fdc4801",
      "sequence": "ffffffff"
    }
  ],
  "outputcount": "01",
  "outputs": [
    {
      "amount": "80a2190000000000",
      "scriptpubkeysize": "19",
      "scriptpubkey": "76a914971802edf585cdbc4e57017d6e5142515c1e502888ac"
    }
  ],
  "locktime": "00000000"
}

Transação: 949591ad468cef5c41656c0a502d9500671ee421fadb590fbc6373000039b693

P2WPKH

Ao destravar um P2WPKH, a assinatura é encontrada junto à chave pública como o primeiro item no campo de testemunha.

Bruto

020000000001013aa815ace3c5751ee6c325d614044ad58c18ed2858a44f9d9f98fbcddad878c10000000000ffffffff01344d10000000000016001430cd68883f558464ec7939d9f960956422018f0702483045022100c7fb3bd38bdceb315a28a0793d85f31e4e1d9983122b4a5de741d6ddca5caf8202207b2821abd7a1a2157a9d5e69d2fdba3502b0a96be809c34981f8445555bdafdb012103f465315805ed271eb972e43d84d2a9e19494d10151d9f6adb32b8534bfd764ab00000000

Separado

{
  "version": "02000000",
  "marker": "00",
  "flag": "01",
  "inputcount": "01",
  "inputs": [
    {
      "txid": "3aa815ace3c5751ee6c325d614044ad58c18ed2858a44f9d9f98fbcddad878c1",
      "vout": "00000000",
      "scriptsigsize": "00",
      "scriptsig": "",
      "sequence": "ffffffff"
    }
  ],
  "outputcount": "01",
  "outputs": [
    {
      "amount": "344d100000000000",
      "scriptpubkeysize": "16",
      "scriptpubkey": "001430cd68883f558464ec7939d9f960956422018f07"
    }
  ],
  "witness": [
    {
      "stackitems": "02",
      "0": {
        "size": "48",
        "item": "3045022100c7fb3bd38bdceb315a28a0793d85f31e4e1d9983122b4a5de741d6ddca5caf8202207b2821abd7a1a2157a9d5e69d2fdba3502b0a96be809c34981f8445555bdafdb01"
      },
      "1": {
        "size": "21",
        "item": "03f465315805ed271eb972e43d84d2a9e19494d10151d9f6adb32b8534bfd764ab"
      }
    }
  ],
  "locktime": "00000000"
}

Transação: 1674761a2b5cb6c7ea39ef58483433e8735e732f5d5815c9ef90523a91ed34a6

Um P2WPKH funciona da mesma forma que um P2PKH, mas usa o campo Testemunha para destravar a entrada, em vez do ScriptSig.

P2TR

Ao destravar uma saída P2TR pelo método padrão de gasto via caminho de chave, a assinatura é o único item no campo de testemunha.

Bruto

02000000000101ec9016580d98a93909faf9d2f431e74f781b438d81372bb6aab4db67725c11a70000000000ffffffff0110270000000000001600144e44ca792ce545acba99d41304460dd1f53be3840141b693a0797b24bae12ed0516a2f5ba765618dca89b75e498ba5b745b71644362298a45ca39230d10a02ee6290a91cebf9839600f7e35158a447ea182ea0e022ae0100000000

Separado

{
  "version": "02000000",
  "marker": "00",
  "flag": "01",
  "inputcount": "01",
  "inputs": [
    {
      "txid": "ec9016580d98a93909faf9d2f431e74f781b438d81372bb6aab4db67725c11a7",
      "vout": "00000000",
      "scriptsigsize": "00",
      "scriptsig": "",
      "sequence": "ffffffff"
    }
  ],
  "outputcount": "01",
  "outputs": [
    {
      "amount": "1027000000000000",
      "scriptpubkeysize": "16",
      "scriptpubkey": "00144e44ca792ce545acba99d41304460dd1f53be384"
    }
  ],
  "witness": [
    {
      "stackitems": "01",
      "0": {
        "size": "41",
        "item": "b693a0797b24bae12ed0516a2f5ba765618dca89b75e498ba5b745b71644362298a45ca39230d10a02ee6290a91cebf9839600f7e35158a447ea182ea0e022ae01"
      }
    }
  ],
  "locktime": "00000000"
}

Transação: 091d2aaadc409298fd8353a4cd94c319481a0b4623fb00872fe240448e93fcbe

As assinaturas em P2TR não usam codificação DER. É por isso que a assinatura desta transação é um pouco mais curta e tem uma aparência um pouco diferente (ou seja, não começa com 30450221) em comparação com as outras assinaturas.

Codificação DER

Diagrama mostrando como converter uma assinatura bruta em codificação DER.

Codificação DER

Serializa os dois números de uma assinatura ECDSA (r e s) no formato DER, usado dentro das transações.

Assinatura
0d
0d
0 bytes

Uma assinatura codificada em DER no Bitcoin normalmente começa com 3044 ou 3045 e geralmente tem 70 ou 71 bytes de comprimento.

Todas as assinaturas ECDSA no Bitcoin usam codificação DER.

DER é um acrônimo para "Distinguished Encoding Rules". Normalmente é usado para codificar estruturas de dados criptográficas (por exemplo, assinaturas) em um formato específico para que possam ser compartilhadas mais facilmente entre computadores.

De qualquer forma, uma assinatura codificada em DER é basicamente só os valores r e s convertidos para valores hexadecimais de 32 bytes, junto com alguns bytes extras de "tipo" e "tamanho" entre eles. Há também uma peculiaridade específica do Bitcoin: se o primeiro byte do valor r ou s for 80 ou maior, você precisa prepender um byte 00 a ele.

Devido ao fato de sempre pegarmos o valor s "baixo" para a assinatura, normalmente você nunca precisará prepender um byte 00 a ele. No entanto, isto é apenas uma regra de padronicidade, então ainda é tecnicamente possível que um valor s "alto" seja minerado, o que poderia levar uma assinatura codificada em DER a até 72 bytes no total.

Os bytes de "tipo" 30 e 02 são sempre os mesmos. Então são apenas os valores de "tamanho", r e s que de fato mudam dentro de uma assinatura codificada em DER.

História

A escolha da codificação DER para assinaturas é um pouco inútil, já que ela não é usada para codificar nenhum outro tipo de estrutura de dados no Bitcoin, então ela só adiciona bytes de dados desnecessários sem benefício real.

Mesmo assim, foi o que Satoshi usou quando projetou o Bitcoin pela primeira vez, então agora temos que usá-la para codificar todas as assinaturas ECDSA. Então, se você está destravando uma saída legada como P2PK, P2PKH, P2MS ou P2SH, ou uma saída segwit como P2WPKH ou P2WSH, suas assinaturas precisam ser codificadas em DER.

Meu palpite é que Satoshi não conhecia os detalhes internos das assinaturas ECDSA e simplesmente usou o que o OpenSSL lhe deu. Se isso não exigisse uma mudança com hard fork (obrigando toda carteira e nó verificador da rede a se atualizar), teríamos mudado isso há muito tempo.
Pieter Wuille, bitcoin.stackexchange.com

No entanto, com a introdução das assinaturas Schnorr na atualização Taproot, decidiu-se não usar mais a codificação DER, então as assinaturas em P2TR não são codificadas em DER.

Assinaturas ECDSA usam codificação DER. Assinaturas Schnorr não.

Tipo de Hash da Assinatura

Um tipo de hash da assinatura (sighash) é usado para indicar quais partes da transação foram assinadas.

Tipos de Hash da Assinatura:

0x01 = SIGHASH_ALL
0x02 = SIGHASH_NONE
0x03 = SIGHASH_SINGLE
0x81 = SIGHASH_ANYONECANPAY | SIGHASH_ALL
0x82 = SIGHASH_ANYONECANPAY | SIGHASH_NONE
0x83 = SIGHASH_ANYONECANPAY | SIGHASH_SINGLE

Um tipo de hash da assinatura de 1 byte é anexado a toda assinatura codificada em DER antes de ser usada em uma transação. Por exemplo, se olharmos esta assinatura:

3045022100c219a522e65ca8500ebe05a70d5a49d840ccc15f2afa4ee9df783f06b2a322310220489a46c37feb33f52c586da25c70113b8eea41216440eb84771cb67a67fdb68c01

Podemos ver que ela termina com um 01, que corresponde ao tipo de sighash SIGHASH_ALL.

Um tipo de sighash de 1 byte é adicionado ao fim de toda assinatura codificada em DER. Um tipo de sighash de 4 bytes também é adicionado ao fim dos dados da transação antes de você assiná-la (veja o passo 8 do algoritmo legado e o passo 5 do algoritmo segwit).

É possível usar os diferentes tipos de sighash para assinar apenas certas partes da transação, o que significa que entradas e/ou saídas podem ser adicionadas à transação depois de você tê-la assinado.

Diagrama mostrando os diferentes tipos de hash da assinatura e como eles podem ser usados para assinar partes diferentes de uma transação.

Por exemplo, você normalmente usa SIGHASH_ALL (0x01) para indicar que a sua assinatura se aplica à transação inteira. Isso significa que nenhuma parte da transação pode ser modificada, senão a assinatura será inválida (e a transação também). É isso que você normalmente quer, porque você normalmente não quer que ninguém mude nada na transação que você assinou.

No entanto, se você assinar uma transação usando SIGHASH_NONE (0x02), isso significa que qualquer pessoa pode adicionar saídas extras à transação mais tarde (antes de ela ser minerada em um bloco), e a assinatura e a transação ainda serão válidas.

Se você não sabe qual tipo de hash da assinatura usar, fique com SIGHASH_ALL (0x01). Este é o tipo de hash da assinatura padrão usado pelas carteiras e protege a transação inteira de ser modificada.

Certos tipos de hash da assinatura precisam ser usados em conjunto com a remoção das partes específicas da transação que você não quer assinar ao criar a assinatura. Então não se esqueça de preparar a transação bruta de acordo com o tipo de sighash escolhido antes de assiná-la.

Resumo

Assinar uma transação é a parte mais complicada de trabalhar com Bitcoin.

Para começar, você tem que dominar a matemática envolvida na criação de uma assinatura. Depois disso, você tem que descobrir como preparar os dados da transação bruta para a assinatura. E se você errar um dos passos do processo, mesmo que ligeiramente, a assinatura será inválida.

Mas é factível.

O truque é fazer o processo um passo de cada vez. Se você tentar programar o algoritmo de assinatura tudo de uma vez, é mais provável que cometa erros, e será difícil descobrir onde você errou e o que precisa fazer para corrigir.

E acredite em mim, você vai cometer erros.

Mas se você dividir o processo em passos e garantir que os seus resultados estejam batendo a cada etapa do caminho, você vai chegar lá eventualmente. E uma vez que você tenha o algoritmo funcionando, pode criar quantas assinaturas quiser, porque uma vez que funciona, funciona para sempre.

Então não deixe que o processo aparentemente complexo o desanime. Embora assinar uma transação dê trabalho na primeira vez, também é incrivelmente satisfatório. E se você conseguir, fará parte de um clube muito pequeno que conseguiu assinar com sucesso a sua própria transação de bitcoin do zero.

Porque, no fim das contas, há duas categorias de programador de Bitcoin: aqueles que assinaram a própria transação e aqueles que não.

Boa sorte.

Recursos