¿Cómo recuperar las direcciones de billetera desde y hacia de una transacción?

Dadas las entradas y salidas de una transacción, me gustaría poder extraer la dirección (o direcciones) de la billetera que envió la transacción, y también la dirección/direcciones que recibirán la transacción.

  • ¿Qué parte/campo de la entrada se refiere al hash de la salida?

  • ¿Y qué parte/campo de la salida se refiere a la dirección que recibirá la salida?

Estoy usando bitcoinj. He intentado mirar las scriptSigentradas y scriptPubKeylas salidas, pero no tiene mucho sentido para mí.

Por ejemplo, para la transacción de génesis: https://blockchain.info/tx/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b?show_adv=true

El scriptPubKey de la salida es[04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f] CHECKSIG

Pero la dirección del receptor es1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa

¿Cómo puedo extraer que la dirección del receptor es la de ese scriptPubKey?

Respuestas (2)

  • ¿Qué parte/campo de la entrada se refiere al hash de la salida?

El punto de salida contiene un txid (32 bytes) y un vout (4 bytes), que especifican la salida que está gastando.

¡Esta no es la dirección de envío!

Lo que hace (cuando no está tratando con una transacción de base de monedas) es buscar la transacción con el txid y ver el resultado de la transacción que corresponde a vout. Entonces, por ejemplo, si vout fuera 3, miraría la cuarta salida de la transacción correspondiente. Si vout fuera 0, miraría la primera salida. La dirección que puede gastar esa salida es la dirección de envío de la transacción original.

La transacción de génesis es una transacción de base de monedas, por lo que txid son todos ceros y vout son todos unos.

  • ¿Y qué parte/campo de la salida se refiere a la dirección que recibirá la salida?

Depende del formato de la salida, pero estas son las reglas generales:

  • Si es P2PK, como aquí, ejecute HASH160 en el primer elemento de scriptPubKey, luego codifíquelo como una dirección.
  • Si es P2PKH, tome el tercer elemento de scriptPubKey y codifique pubkeyhash como una dirección.
  • Si es P2SH, tome el segundo elemento de scriptPubKey y codifíquelo como una dirección con Address.fromP2SHHash.

Entro en más detalles sobre los posibles tipos de direcciones aquí: ¿Qué formularios de script de bitcoin deben detectarse al rastrear el saldo de la billetera?

¿Cómo puedo extraer que la dirección del receptor es la de ese scriptPubKey?

Hacer esto:

import org.bitcoinj.core.*;
import org.bitcoinj.params.MainNetParams;

import org.spongycastle.crypto.digests.RIPEMD160Digest;

import java.nio.file.Files;
import java.io.File;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class Test {
    public static void main(String[] args) throws Exception {
        byte[] b;
        NetworkParameters np = MainNetParams.get();
        Context.propagate(new Context(np));
        b = Files.readAllBytes(new File("genesis.bin").toPath());
        Transaction tx1 = new Transaction(np, b);
        System.out.println(tx1);
        byte[] pk = tx1.getOutput(0).getScriptPubKey().getPubKey();
        System.out.println(bytesToHex(pk));
        System.out.println(bytesToHex(hash160(pk)));
        Address a = new Address(np, hash160(pk));
        System.out.println(a);
    }
    static byte[] hash160(byte[] in) {
        MessageDigest d1;
        try {
            d1 = MessageDigest.getInstance("SHA-256");
        } catch(NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        d1.update(in);
        byte[] digest = d1.digest();
        RIPEMD160Digest d2 = new RIPEMD160Digest();
        d2.update(digest, 0, 32);
        byte[] ret = new byte[20];
        d2.doFinal(ret, 0);
        return ret;
    }
    final protected static char[] hexArray = "0123456789abcdef".toCharArray();
    public static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for ( int j = 0; j < bytes.length; j++ ) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }
}

(Tenga en cuenta que tengo un archivo en mi directorio de trabajo actual llamado genesis.bin, que contiene los bytes sin procesar para la transacción de génesis).

Y obtienes:

  4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
     == COINBASE TXN (scriptSig PUSHDATA(4)[ffff001d] PUSHDATA(1)[04] PUSHDATA(69)[5468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73])  (scriptPubKey PUSHDATA(65)[04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f] CHECKSIG)

04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f
62e907b15cbf27d5425399ebf6f0fb50ebb88f18
1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa

¿Cuál es la dirección que estábamos tratando de obtener?

Gracias, muy útil. ¿Cómo sé si una salida es P2PK, P2PKH o P2SH? Y, ¿estos 3 formatos cubren todos los formatos posibles en los que puede estar? Además, ¿cómo sabría si es una tarifa de minería sin una dirección? Por último, para obtener el txid de entradas con Bitcoinj, ¿usaría el método input.getParentTransaction?
Vi su respuesta en bitcoin.stackexchange.com/questions/35456/… que responde a mi primera pregunta. Preguntas restantes: 1) ¿Supongo que la dirección del remitente de txns multi-sig no se puede recuperar? 2) ¿Conoce los hashes de txns usando P2PKH y P2SH para que pueda probar mi análisis de ellos? 3) ¿Cómo puedo recuperar el txId y el vout de las entradas a través de Bitcoinj? Si hago que esto funcione, lo enviaré como PR a bitcoinj. Gracias.
¿Sería mejor si creara una nueva pregunta para cada una de estas?
@ClickUpvote 1) Eso es posible. Podría querer decir "qué direcciones poseían este Bitcoin antes" o "qué direcciones realmente firmaron esto". La primera se puede responder mirando el scriptPubKey anterior, la segunda requiere el scriptPubKey anterior y el scriptSig actual. 2) P2PKH/P2SH 3) tx.getInput(vin).getOutpoint() seguido de getHash o getIndex. PR) Genial! Sería bueno ver eso en bitcoinj. PD) Haría preguntas separadas. Hay un límite de 600 caracteres en los comentarios.
¿Se input.getOutput.getHashrefiere a la identificación de txn y getIndexse refiere al vout de la salida de ese txn? PD muchas gracias, llevo unos dias tratando de obtener respuestas a estas preguntas, eres la primera persona que ha podido responderlas. :)
¿Es posible descargar los datos binarios de esos txns desde blockchain.info u otro método, sin tener que atravesar la cadena buscándolos?
@ClickUpvote Debería poder solicitar la página web https://blockchain.info/tx/<txid here>?format=hexpara obtener una versión codificada en hexadecimal. Otras API tienen algo similar.
Entonces, por ejemplo, blockchain.info/tx/… , que se puede guardar como un archivo .bin y pasar a Transaction como en su código de ejemplo. ¿O es necesario hacer algo más con los datos hexadecimales?
@ClickUpvote Tiene que convertirse de hexadecimal a bytes sin formato. stackoverflow.com/questions/8890174/…

El scriptPubKey contiene la clave pública utilizada para sintetizar la dirección pública. Aquí hay dos enfoques pedantes, sin usar bitcoinj, aplicando comandos bx para sintetizar la dirección pública de interés anterior.

% echo 04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f | bx sha256 | bx ripemd160 | bx base58check-encode

1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa

% echo 04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f | bx ec-to-address

1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa