์ผ๋ฐ์ ์ผ๋ก ๋ธ๋ก ํ์๊ธฐ(block explorers)๊ฐ ์ ๊ณตํ๋ ์น API๋ฅผ ์ฌ์ฉํ๋ฉด ์ข๋ ๋น ๋ฅด๊ฒ ์์ํ ์ ์์ต๋๋ค.
์ด ์ฑ
์์ ์ด๋ฏธ QBitNinja๋ฅผ ์ฌ์ฉํ์ง๋ง ๋ ๋ง์ด ์ฌ์ฉ ํ๊ฒ ๋ฉ๋๋ค.
๋ธ๋กํ์๊ธฐ๋ ์ฒด์ธ์ ๋ธ๋ก, ํธ๋์ ์
๋ฐ ์ฃผ์์ ๋ํ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ ์์ฒด ํธ์คํ
๋๋ ํ์ฌ ํธ์คํ
์๋ฃจ์
์
๋๋ค.
๋ธ๋ก ํ์๊ธฐ๋ ๋นํธ ์ฝ์ธ ๋
ธ๋์ ์ฐ๊ฒฐํ๊ณ ๋ธ๋ก ์ฒด์ธ์ ๋ฐ์ดํฐ๋ฅผ ์ธ๋ฑ์ฑํ๊ณ ์ฌ์ฉํ๊ธฐ ์ฌ์ด API๋ฅผ ๋
ธ์ถํฉ๋๋ค.
์๋ฃจ์
์๋ QBitNinja, Blockcypher, Smartbit, Electrum server, Insight, NBXplorer๋ฑ์ด ์์ต๋๋ค.
์ฅ์ :
- Bitcoin Core RPC๋ณด๋ค ๋ ์ข์ API,
- ๋ ๋ง์ ๋ถํ(load)๋ฅผ ์ฒ๋ฆฌ ํ ์ ์์ต๋๋ค.
- ๋ง์ ์์ ์ง๊ฐ์ ์ง์ํ๊ณ ๋์ ์ผ๋ก ์ถ๊ฐ ํ ์ ์์ต๋๋ค.
- ๋น ๋ฅธ ํด๋ผ์ด์ธํธ-์๋ฒ ์ํคํ ์ฒ
๋จ์ :
- ํ์ฌ์์ ํธ์คํ ๋๋ ๋ฌธ์ ์๋ ํฌํฌ(fork)๊ฐ ๋ฐ๊ฒฌ๋ ๊ฒฝ์ฐ ํฌํฌ(fork) ์ ํ๊ถ์ด ์์ต๋๋ค.
- ๋๋ก๋, ๊ทธ๋ค์ ์๋น์ค๊ฐ ์ ์ฒด ์ง๊ฐ์ ํ์ํ ๋ชจ๋ ๊ฒ์ ์ฒ๋ฆฌํ๊ธฐ์ ์ถฉ๋ถํ์ง ์์ต๋๋ค.
- ์กด์ฌํ์ง ์๋ ๊ฐ์ธ์ ๋ณด: ์๋ฒ๋ ํด๋ผ์ด์ธํธ์ ๋ํ ๋ชจ๋ ๊ฒ์ ์๊ณ ์์ต๋๋ค. ์์ฒด ํธ์คํ ์ ํ์๋ ์ ์ฉ๋์ง ์์ต๋๋ค.
์๋ก ๋ค๋ฅธ ๋ธ๋ก ํ์๊ธฐ๋ ๋ค๋ฅธ API์ ๊ธฐ๋ฅ์ ๋ ธ์ถํฉ๋๋ค. ์๋ฅผ ๋ค์ด ๋๋ถ๋ถ์ ๋ธ๋ก ํ์๊ธฐ๋ HTTP ์น API๋ฅผ ์ฌ์ฉํ๋ ๋ฐ๋ฉด Electrum์ the Stratum ํ๋กํ ์ฝ์ ์ฌ์ฉํฉ๋๋ค. ๋ธ๋ก ํ์๊ธฐ๋ ์ง๊ฐ์ ๊ฐ์ธ ํค๋ฅผ ๊ฐ์ง๊ณ ์์ง ์์ต๋๋ค.
QBitNinja์์๋ ํญ์ ์ฃผ์๋ฅผ ๋ณ๊ฒฝํ๋ ์ง๊ฐ์ธ ๊ฒฝ์ฐ ์ถ์ ํ๊ธฐ๊ฐ ์ด๋ ต์ต๋๋ค.
๋์ผํ ์ง๊ฐ์ ์ํ ๋ชจ๋ ์ฃผ์๋ฅผ ํด๋งํ์ฌ ๋ณ๊ฒฝ ์ฌํญ์ ๊ฐ์งํด์ผ ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
๊ทธ๋ฌ๋ Electrum ๋๋NBXplorer ๋ฐ SmartBit์ ์น์์ผ(websockets) ๋๋ ๋กฑํด๋ง(long polling)์ ํตํด ์๋ฆผ์ ๋
ธ์ถํ๋ฏ๋ก ์ง๊ฐ์ ๋ชจ๋ ์ฃผ์๋ฅผ ํด๋ง ํ ํ์๊ฐ ์์ต๋๋ค.
Insight๊ฐ ์ ์ ์ง๋์ง ์์ต๋๋ค. Blockcypher,QBitNinja ๋ฐ Smartbit๋ ํ์ฌ์์ ํธ์คํ
๋ฉ๋๋ค.
์ด๋ฌํ ๋ฐฉ์์ผ๋ก ์ง๊ฐ์ ๊ตฌ์ถํ๋ ๋ฐ ๊ด์ฌ์ด ์๋ค๋ฉด nopara73์ CodeProject ๊ธฐ์ฌ๋ฅผ ์ฐธ์กฐํ์ญ์์ค: Build your own Bitcoin wallet with QBitNinja in C#
NBxplorer๋ ๋งค์ฐ ๊ฐ๋จํ API๋ฅผ ๊ฐ๋๋ก ๋ง๋ค์ด์ก์ผ๋ฉฐ, ์์ฒด ํธ์คํ
์ด ๊ฐ๋ฅํ๋ฉฐ, ์ง๊ฐ์ ํ์ํ ๊ฒ๋ง ์ถ์ ํฉ๋๋ค.
QBitNinja์๋ ๋ฌ๋ฆฌ ํ๋
ธ๋(full node)๊ฐ ์์ด์ผ ํ์ง๋ง, ์น์์ผ ์๋ฆผ์ ์ ๊ณตํ๊ณ , ์ง๊ฐ ์์ก์ ์ฝ๊ฒ ์กฐํ ํ ์ ์์ต๋๋ค.
NBXplorer๋ ๋ํ ๋จ์ผ ์๋ฒ์์ ๋ค์ค ์ํธํ ํตํ์ ๋๋ค. 2018 ๋ 10 ์ ํ์ฌ ๋นํธ ์ฝ์ธ, ๋ผ์ดํธ ์ฝ์ธ, BCash, BGold, Dash, Dogecoin, Dystem, Feathercoin, Groestlcoin, Monacoin, Polis, UFO, Viacoin ๋ฐ Zclassic๋ฅผ ์ง์ ํฉ๋๋ค.
๋ํ, 'NBitcoin'๊ณผ ์ํํ๊ฒ ํตํฉ๋ฉ๋๋ค.
NBXplorer๋ฅผ ์ค์ ํ๋ ค๋ฉด ๊ธฐ๋ณธ ๋งค๊ฐ ๋ณ์๊ฐ ์๋ ์์ ํ ๋๊ธฐํ ๋ bitcoind ๋
ธ๋๊ฐ ํ์ํฉ๋๋ค.
๊ทธ๋ฐ ๋ค์ ๊ธฐ๋ณธ ๋งค๊ฐ ๋ณ์๋ก NBXplorer๋ฅผ ๋ณต์ ํ๊ณ ์คํํฉ๋๋ค.
NBXplorer.Client nuget package๋ฅผ ์ฐธ์กฐํ ๋ค์ NBXplorer์ ์ฌ์ฉ์ ์ง๊ฐ์ ์ถ์ ํ๋๋ก ์๋ ค์ผํฉ๋๋ค:
var network = new NBXplorerNetworkProvider(NetworkType.Mainnet).GetBTC();
var userExtKey = new ExtKey();
var userDerivationScheme = network.DerivationStrategyFactory.CreateDirectDerivationStrategy(userExtKey.Neuter(), new DerivationStrategyOptions()
{
// Use non-segwit
Legacy = true
});
ExplorerClient client = new ExplorerClient(network);
client.Track(userDerivationScheme);Testnet ๋๋ Regtest๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด NetworkType.Mainnet ๋ฌธ์ฅ์ ๋ณ๊ฒฝํ์ญ์์ค.
์ฌ์ฉํ์ง ์์ ์ ์ฃผ์๋ฅผ ์ํ๋ ๊ฒฝ์ฐ:
Console.WriteLine(client.GetUnused(userDerivationScheme, DerivationFeature.Deposit).Address);๊ทธ๋ฐ ๋ค์ ์ฌ์ฉ์์ UTXO๋ฅผ ์ฟผ๋ฆฌํ๊ณ ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค:
var utxos = client.GetUTXOs(userDerivationScheme, null, false);ํด๋น UTXO๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด:
var coins = utxos.GetUnspentCoins();
var keys = utxos.GetKeys(userExtKey);
TransactionBuilder builder = Network.Main.CreateTransaction();
builder.AddCoins(coins);
builder.AddKeys(keys);
builder.Send(new Key(), Money.Coins(0.5m));
builder.SetChange(changeAddress.ScriptPubKey);
// Set the fee rate
var fallbackFeeRate = new FeeRate(Money.Satoshis(100), 1);
var feeRate = tester.Client.GetFeeRate(1, fallbackFeeRate).FeeRate;
builder.SendEstimatedFees(feeRate);
/////
var tx = builder.BuildTransaction(true);
Console.WriteLine(client.Broadcast(tx));์ด ์๋ฃจ์ ์ ๋ฌธ์ ์ ์ ๋์์ ๋๋ฒ ํธ์ถํ๋ฉด, ๋์ผํ ์ฝ์ธ์ ์๋นํ๋ ๋ ๊ฐ์ ํธ๋์ญ์ ์ ๋ธ๋ก๋์บ์คํ ํ๋ฉด, ํธ๋์ญ์ ์ค ํ๋๊ฐ ์ญ์ ๋๋ค๋ ๊ฒ์ ๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ๋ ค๋ฉด ๋์ผํ ๋์ ์ ๋ ๋ฒ ์ฌ์ฉํ์ง ์๋๋กํด์ผ ํฉ๋๋ค.
๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ๊ฐ๋จํ ์ฌ ์๋ํ๋ ๊ฒ์ ๋๋ค:
while(true)
{
var coins = utxos.GetUnspentCoins();
var keys = utxos.GetKeys(userExtKey);
TransactionBuilder builder = Network.Main.CreateTransactionBuilder();
builder.AddCoins(coins);
builder.AddKeys(keys);
builder.Send(new Key(), Money.Coins(0.5m));
builder.SetChange(changeAddress.ScriptPubKey);
// Set the fee rate
var fallbackFeeRate = new FeeRate(Money.Satoshis(100), 1);
var feeRate = tester.Client.GetFeeRate(1, fallbackFeeRate).FeeRate;
builder.SendEstimatedFees(feeRate);
/////
var tx = builder.BuildTransaction(true);
var result = client.Broadcast(tx);
if(result.Success)
{
Console.WriteLine("Success!");
break;
}
else if(result.RPCCode.HasValue && result.RPCCode.Value == RPCErrorCode.RPC_TRANSACTION_REJECTED)
{
Console.WriteLine("We probably got a conflict, let's try again!");
continue;
}
else
{
Console.WriteLine($"Something is really wrong {result.RPCCode} {result.RPCCodeMessage} {result.RPCMessage}");
// Do something!!!
}
}๋ ๋ค๋ฅธ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ ํ์ธํ ์ ์๋ ์ด๋ฏธ ์ฌ์ฉ ๋ ์์ํฌ์ธํธ(outpoint)์ ์ ์ฒด ๋ชฉ๋ก์ ๋ง๋๋ ๊ฒ์ ๋๋ค.
