[블록체인] 비트코인 트랜잭션 서명하기

반응형

UTXO 조회하기

서명에 필요한 utxo를 테스트넷에서 가져옵니다. 가져온 utxo에서 전송에 필요한 금액 만큼만 사용하면 됩니다.

import axios from 'axios';

const Client = axios.create({
  baseURL: 'https://test-insight.bitpay.com/api',
  headers: { "Content-Type": "application/json" },
  timeout: 10000
});

const fromAddress = "1J5RoyfyjLBcdGc2PCnN8wkfEUJms13vv8";
const utxos = await Client.post(`/addrs/utxo`, { addrs: fromAddress }).then(r => r.data);

실제 비트코인 주소입니다. 메인넷에서 테스트로 송금해주셔도 됩니다.

 

트랜잭션 생성하기

트랜잭션 생성에는 비트코인(bitcore-lib) 라이브러리를 사용하였습니다. 그리고 일단은 P2PKH 주소 서명만 예제 코드로 사용하였습니다. 주소의 prefix로 스크립트 유형을 알아낼 수 있습니다. 비트코인 주소 유형은 이곳 위키 를 참고하세요.

const bitcore = require('bitcore-lib');
const tx = new bitcore.Transaction();
if ( ["1", "m", "n"].includes(fromAddress.slice(0, 1)) ) { // P2PKH
  tx.from(utxos); 
} else {
  // P2PK, P2SH, P2WPKH, Etc ...
}

tx.to(to, amount.toNumber());
tx.feePerKb(20000); // TX KB별 필요한 수수료 입력
tx.change(fromAddress); // 잔금 받을 주소 입력(HD지갑이라면 change 주소 사용)

const transaction = tx.uncheckedSerialize();

주소 prefix가 1이면(테스트넷은 m or n) P2PKH 입니다.

 

서명할 시그해시 계산하기

const hash = tx.inputs.map((input, index) => {
  let sighash = bitcore.Transaction.Sighash.sighash(tx, 0x01, index, input.output.script);
  sighash = bitcore.encoding.BufferReader(sighash).readReverse();
  return sighash.toString("hex");
});

bitcore 라이브러리는 서명에 little-endian을 사용하고 있습니다. 그래서 계산된 hash를 다시 reverse 합니다.

 

트랜잭션에 서명값 추가하기

외부 서버나 디바이스에서 받아온 서명을 다시 트랜잭션에 추가합니다.

// sign 서버에 서명 요청하기
const signatures = await SignService.requestSignature(hash); 

signatures.forEach((signature, index) => {
  // signature 객체 생성
  let sig = bitcore.crypto.Signature.fromCompact(
    buffer.Buffer(signature, "hex")
  );

  // 시그해시 계산
  const input = tx.inputs[index];
  let hashbuf = bitcore.Transaction.Sighash.sighash(tx, 0x01, index, input.output.script);
  hashbuf = bitcore.encoding.BufferReader(hashbuf).readReverse();

  // 서명 검증
  const ecdsa = bitcore.crypto.ECDSA({ hashbuf, sig });
  ecdsa.set({ pubkey: ecdsa.toPublicKey() });
  if (!ecdsa.verify().verified) {
    return Errors.BAD_SIGNATURES;
  }

  // tx에 서명 추가
  const signatureObj = new bitcore.Transaction.Signature({
    publicKey: ecdsa.pubkey, 
    prevTxId: input.prevTxId,
    outputIndex: input.outputIndex,
    inputIndex: index,
    signature: sig,
    sigtype: 0x01 // SIGHASH_ALL.
  });

  tx.applySignature(signatureObj);
});

// 서명된 tx를 serialize 하기
const serializeTx = tx.serialize();


이제 마지막으로 serialized된 tx를 비트코인 노드서버로 send 하면 됩니다.

 

반응형