bitcoinj 로 내가 사용할 비트코인 지갑을 만들어보자 (6) - bitcoinj-devsteemCreated with Sketch.

in #kr7 years ago

전에 비트코인 전송에 대해서 살펴보았습니다. 오늘은 수수료 처리에 대해서 살펴보겠습니다.

비트코인 수수료는 다음과 같이 결정됩니다.

첫번째는 채굴자가 처리하는 바이트 수에 대한 요금입니다. 채굴자들은 더 높은 수수료를 지불하는 거래에 대해서 우선적으로 채굴을 하려고 합니다. 따라서 채굴자들이 채굴하는 평균 처리 수수료보다 낮은 수수료를 지불하면 거래가 늦어지거나 아예 채굴이 안되어 거래가 승인이 안될 수도 있습니다.

두번째는 사용자가 거래 수수료를 결정하는 것입니다.

그렇다면 앞서 살펴보았던 tx id a016f2821c413279a90b68b6be8d1e7732a28d1f06a72c38d2055aa47e17e66a 를 통해서 수수료를 확인해 봅니다. (링크 https://testnet.blockchain.info/tx/a016f2821c413279a90b68b6be8d1e7732a28d1f06a72c38d2055aa47e17e66a)

블록체인인포 테스트넷 익스플로러에서 해당 거래에 대해 살펴봅니다. 전체 크기는 225 bytes이며 수수료로 0.00227 BTC가 사용되었습니다. 1 바이트당 수수료는 100.889 sat/B 입니다. 거래량은 0.01 BTC 입니다. 따라서 거래에 사용된 총 금액은 0.01227 BTC 입니다.

이체 승인이 처리되기 까지 걸린 시간을 살펴봅니다. 수신 시각은 2018-05-07 23:53:37 이며, 블록에 포함된 시간은 2018-05-07 23:56:29 입니다. 약 3분 정도 시간이 소요되었습니다.

이제 다른 거래 내역을 살펴봅니다. (링크 https://testnet.blockchain.info/ko/tx/8e8c9112c7661d49f0032e8b1752627d5c0e933acb412ab1ec92fd2f9c3c5fb1)

전체 크기는 249 bytes이며 수수료로 0.00100001 BTC가 사용되었습니다. 1 바이트당 수수료는 401.61 sat/B 입니다. 거래량은 0.55 BTC 입니다. 따라서 거래에 사용된 총 금액은 0.55100001BTC 입니다.

이체 승인이 처리되기 까지 걸린 시간을 살펴봅니다. 수신 시각은 2018-05-07 02:39:37 이며, 블록에 포함된 시간은 2018-05-07 02:39:45 입니다. 약 8분 정도 시간이 소요되었습니다.

테스트넷이라 메인넷과 다소 다를 수 있습니다.

이제 1 바이트 당 수수료를 살펴봅니다. 수수료는 249 bytes * 401.61 sat/B 입니다. 계산을 하면 100000.89 sat 나옵니다. BTC로 계산하면 0.00100001 (반올림) 이 됩니다. 수수료 계산이 이해 되십니까?

수수료를 살펴보면 첫번째 거래는 3분의 시간이 소요되었으며 두번째 거래는 8분의 시간이 소요되었습니다. 보다 빠른 거래를 위해서는 적절한 수수료를 지불해야 거래가 빠르게 처리된다는 것을 확인할 수 있습니다.

현재 거래 수수료의 상태를 확인해 봅니다. (메인넷 기준, Earn Bitcoin 링크 https://bitcoinfees.earn.com/)

수수료를 보는 방법은 위에 숫자는 비승인 거래 숫자이며 아래 숫자는 승인 거래 숫자입니다. 따라서 미승인 거래 숫자가 적을 수록 빠르게 승인이 처리되는 것입니다.

아래 8/422 경우 미 승인 처리 숫자가 가장 적습니다. 비용은 80 satoshi 입니다. 위 거래로 계산을 해보면, 249 bytes * 80 sat/B 으로 19920 satoshi를 지불해야 합니다. BTC로 계산을 해보면 0.00019920 BTC 입니다.

그렇지만 이 비용이 비싸다고 생각된다면 Delay가 가장 적은 284/26016 으로 비용을 지불할 수 있습니다. 비용은 30 satoshi 입니다. 다시 계산을 해보면, 249 bytes * 30 sat/B 으로 7470 satoshi가 됩니다. BTC로 계산을 해보면 0.00007470 입니다. 시간도 25분 이내에 처리됨을 알 수 있습니다.

만약 가장 적은 비용과 가장 높은 비용이 걱정이 된다면 중간 비용으로 사용자가 처리할 수도 있습니다.

자 이제 프로그램에 수수료 계산 프로그램을 만들어 봅니다.

전에 작성한 코드를 사용하지 않을 것입니다. 전에 작성한 코드를 주석 처리 합니다.

Address address = Address.fromBase58(myWallet.getTestNetParam(), sendAddress);
Transaction tx = kit.wallet().createSend(address, Coin.parseCoin("0.01"));
final Peer peer = kit.peerGroup().getConnectedPeers().get(0);
peer.sendMessage(tx);

함수 이름을 정의합니다.

public void setSendRequest (NetworkParameters params, String base58, String value, Wallet wallet, int fee) throws InsufficientMoneyException

전송할 주소를 생성합니다.

Address address = Address.fromBase58(getTestNetParam(), base58);

전송할 코인 값을 받습니다.

if (Float.parseFloat(value) > 0.0f) {
SendRequest request = SendRequest.to(address, Coin.parseCoin(value));
request.shuffleOutputs = false;
request.ensureMinRequiredFee = true;

이제 수수료를 처리하는 로직을 작성합니다.

      switch(fee) {
          case 0: 
              request.feePerKb = Transaction.DEFAULT_TX_FEE;
              break;
          case 1: 
              request.feePerKb = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
              break;
          default:
              request.feePerKb = Transaction.MIN_NONDUST_OUTPUT; 
              break;
      }

수수료는 3가지 방식으로 처리할 수 있습니다. (여기서 사용자 입력 방식은 제외하였습니다.)
먼저 Transaction.DEFAULT_TX_FEE 입니다. 가장 기본 수수료입니다. bitcoinj에서 해당 소스를 확인하면 수수료는 1mBTC 입니다.

public static final Coin DEFAULT_TX_FEE = Coin.valueOf(100000); // 1 mBTC

두번째 Transaction.REFERENCE_DEFAULT_MIN_TX_FEE 입니다. bitcoinj에서 해당 소스를 확인하면 수수료는 0.01mBTC 입니다.

public static final Coin REFERENCE_DEFAULT_MIN_TX_FEE = Coin.valueOf(1000); // 0.01 mBTC

수수료가 Transaction.DEFAULT_TX_FEE 비해 1/100 수준입니다.

마지막으로 Transaction.MIN_NONDUST_OUTPUT 입니다. 546 sat/B 입니다. 대략 계산을 하면, 246 * 546 이므로 135954 입니다. 대략 0.00135954 BTC 입니다.

3가지 수수료를 비교하면, 다음과 같습니다.

DEFAULT_TX_FEE = 0.00100000 BTC
REFERENCE_DEFAULT_MIN_TX_FEE = 0.00001000 BTC
MIN_NONDUST_OUTPUT = 0.00135954 BTC

입니다. 위에 메인넷 전송 상태를 보면 0.00019920 BTC 로 충분한 수수료가 되는 것을 알 수 있습니다. REFERENCE_DEFAULT_MIN_TX_FEE의 2배 수수료면 충분할 것으로 보입니다. 수수료는 상황에 따라 변동되기 때문에 가장 좋은 선택은 DEFAULT_TX_FEE으로 안정적으로 보내는 것입니다.

BTC 환율을 계산해보면 대략 1만원 정도의 수수료가 발생됨을 알 수 있습니다. (링크 https://kr.investing.com/crypto/bitcoin/btc-krw-converter)

이제 코인을 전송합니다.

SendResult sendResult = wallet.sendCoins(request);

전체 소스는 다음과 같습니다.

public void setSendRequest(NetworkParameters params, String base58, String value, Wallet wallet, int fee) throws InsufficientMoneyException
{
Address address = Address.fromBase58(getTestNetParam(), base58);

  if (Float.parseFloat(value) > 0.0f) {           
      SendRequest request = SendRequest.to(address, Coin.parseCoin(value));
      request.shuffleOutputs = false;
      request.ensureMinRequiredFee = true;

      switch(fee) {
          case 0: 
              request.feePerKb = Transaction.DEFAULT_TX_FEE;
              break;
          case 1: 
              request.feePerKb = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
              break;
          default:
              request.feePerKb = Transaction.MIN_NONDUST_OUTPUT; 
              break;
      }
      SendResult sendResult = wallet.sendCoins(request);

}

이제 main() 메소드에서 setSendRequest() 메소드를 호출합니다.

myWallet.setSendRequest(myWallet.getTestNetParam(), sendAddress, "0.1", kit.wallet(), 0);

위 호출을 보면, 0.1 BTC를 sendAddress로 보냅니다. 이때 수수료는 0 번 Transaction.DEFAULT_TX_FEE; 로 보냅니다.

거래를 전파하기 위해 PendingTransaction을 확인하여 Peer에 전송합니다.

  Collection<Transaction> pendingTxs = kit.wallet().getPendingTransactions();
      final Peer peer = kit.peerGroup().getConnectedPeers().get(0);
      for (Transaction t : pendingTxs) {
          peer.sendMessage(t);
      }

처음 거래를 만들면 거래는 SELF의 Pending 상태가 됩니다. n3F4HPZ6MmZZLTG11faas7joKm4fDLfQrh 주소로 비트코인을 보내봅니다.

전송을 하면 다음과 같이 Pending 정보를 Wallet에서 확인할 수 있습니다.

PENDING:
-0.100227 BTC total value (sends 0.429319 BTC and receives 0.329092 BTC)
confidence: Pending/unconfirmed. Source: SELF
4948a84be4eea75ddb7a82f992dd63d003cd3754f85f8c18e5303da775f8e9df
updated: 2018-05-13T14:28:48Z
in PUSHDATA(72)[3045022100d88108714284f4d9fcbf908962314c98eecd67a4c8d5223cb92fdddd84f3b95e02205a26d13096b45b6ccac4ad59d7dd5961e692eaa229a957f04d7877b6e60aeeea01] PUSHDATA(33)[02c8b9d7877994770286bc8e3314e9e8c872610afa5dde1d44b3cadc9e2010423c] 0.429319 BTC
outpoint:8a6b874b2306951ec88d4b558219d96736954701e94c01264c2c579df67485d2:0 hash160:7d33a317b0212763138878e0a2ce9db1a64eadd3
out DUP HASH160 PUSHDATA(20)[ee511c600cf13d769102c144a0a8b1533475c79d] EQUALVERIFY CHECKSIG 0.10 BTC
out DUP HASH160 PUSHDATA(20)[6df179cf1959bfd9567b5108a73d077e53185391] EQUALVERIFY CHECKSIG 0.329092 BTC
fee 0.00100442 BTC/kB, 0.000227 BTC for 226 bytes
prps USER_PAYMENT

블록사이퍼에서도 확인할 수 있습니다. 아직 0/6 confirmations 상태입니다.

(링크 https://live.blockcypher.com/btc-testnet/address/n3F4HPZ6MmZZLTG11faas7joKm4fDLfQrh/)

여기까지 비트코인 지갑을 만들고 수신, 수수료 처리에 대해서 알아보았습니다. 여기까지 같이해주셔서 감사합니다.

다음에 더 좋은 주제로 다시 연재해보도록 하겠습니다.

(마음에 드셨다면 맞팔과 보팅 부탁드립니다.^^)

Sort:  

실질적으로 도움이 되는 예제와 코드입니다. 다음 포스팅도 기대하겠습니다.

감사합니다. 다음에 더 좋은 주제를 준비해보겠습니다.

꼭필요하면서 쉽게 따라할수 있고
암호화폐 하는 사람이면 누구나 공감할수 있는 소재로 글 부탁합니다.
1만댓글 실천중 ㅋㅋ

네. 프로그래밍으로 이야기를 하려고 합니다. 이론에 대한 글은 이미 너무 많아서요. 이론과 실제 프로그래밍을 하면서 느끼는 거리감은 좀 꽤 됩니다. 좋은 의견 주셔서 감사합니다.

You received 0.51 % upvote as a reward From round 1 on 2018.05.17. Congrats!

Thank you!