ํธ๋์ญ์ ์ ๋ํ ์์๋ฃ๋ ์ด๋ป๊ฒ ๊ณ์ฐ๋๋๊ฐ?
Transaction Fee
- ๋ณด์์ด ์ฃผ์ด์ง์ง ์์ผ๋ฉด ๋๊ฐ ์ผ์ ํ ๊น?
- ์ฑ๊ตด์๋ ๊ฑฐ๋ ๋ด์ญ์ ์ ๋ฆฌํ๊ณ ๋ธ๋ก์ ๋ง๋ค์ด ์ถ๊ฐํจ์ผ๋ก์ ์์๋ฃ๋ฅผ ๋ฐ๋๋ค.
- (๋ฌผ๋ก ์ถ๊ฐ์ ์ผ๋ก ์ฑ๊ตด๋ณด์๋ ์๋ค.)
- ๊ทธ๋ ๋ค๋ฉด ์ด ์์๋ฃ๋ ๋๊ฐ ์ง๋ถํ๋ ๊ฑธ๊น?
- ํธ๋์ญ์ ์ ๋ง๋ค์ด ๋ณด๋ผ ๋ ์ด ์์๋ฃ๋ฅผ ์ถ๊ฐํด์ ๋ณด๋ด๋ฉด ๋๋ค.
- ๊ทธ๋ ๋ค๋ฉด, ํธ๋์ญ์ ์ ์ ๋ ฅ๊ณผ ์ถ๋ ฅ์ผ๋ก ๊ตฌ์ฑ๋์๋ค ํ๋๋ฐ, ์ฌ๊ธฐ์ ์์๋ฃ๋ฅผ ์ถ๊ฐํ๋ ค๋ฉด
- ์ ๋ ฅ์ ํฉ์ด ์ถ๋ ฅ์ ํฉ๋ณด๋ค ๋ ํฌ๋ฉด ๋๋ค.
- ๊ทธ๋ฐ๋ฐ, ์ ๋ ฅ์ ๋ํด ๋ฐฐ์ ์ ๋ ์ ๋ ฅ์ ๋ํ ๊ธ์ก์ ํ๋์ ์์๋ค. (์ถ๋ ฅ์๋ ์์๋ค.)
- ๊ทธ๋ฌ๋ฉด ์ ๋ ฅ์ ๊ธ์ก์ ์ด๋ป๊ฒ ์ ์ ์์๊น?
- ์ ๋ ฅ์ผ๋ก ๋ค์ด์จ ๊ฒ๋ค์ ๋ํด UTXO๋ฅผ ์ฐพ์์ ๊ธ์ก์ ํ์ธํ๋ฉด ๋๋ค.
- ์ ๋ ฅ์๋ ์ด์ ํธ๋์ญ์ hex๊ฐ ์๊ณ , ์ด๊ฑธ๋ก ํ๋ ธ๋๋ก ๋ถํฐ ์ฐพ์ ๋ค, ๊ฑฐ๊ธฐ์ output์ ํด๋นํ๋ ํ์ฌ์ input์ ์ฐพ์ ๊ธ์ก์ ์ฝ์ผ๋ฉด ๋๋ค.
- ํ๋ ธ๋๋ผ๋ฉด ๋ฐ๋ก ์ฐพ์ผ๋ฉด๋๊ณ , ์๋๋ผ๋ฉด ๋ฏฟ์ ์ ์๋ ์ 3์๊ฐ ์ ๊ณตํ๋ ํ๋ ธ๋๋ก ๋ถํฐ ์ ๋ณด๋ฅผ ์ป์ด์ผ ํ๋ค.
- ๊ทธ๋ฌ๋ ค๋ฉด ์ ๋ ฅ์ ์ ํ์๋ ์ด์ ํธ๋์ญ์ hex๋ก ์ ๋ ฅ์ด ๊ณผ๊ฑฐ์ output์ผ๋ก ์์๋ transaction์ ๊ฐ์ ธ์์ผ ํ๋ค.
- ์ด๋ฅผ ์ํ ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณด์.
class TxFetcher:
cache = {}
@classmethod
def get_url(cls, testnet=False):
if testnet:
return 'https://blockstream.info/testnet/api/'
else:
return 'https://blockstream.info/api/'
@classmethod
def fetch(cls, tx_id, testnet=False, fresh=False):
if fresh or (tx_id not in cls.cache):
url = '{}/tx/{}/hex'.format(cls.get_url(testnet), tx_id)
response = requests.get(url)
try:
raw = bytes.fromhex(response.text.strip())
except ValueError:
raise ValueError('unexpected response: {}'.format(response.text))
if raw[4] == 0:
raw = raw[:4] + raw[6:]
tx = Tx.parse(BytesIO(raw), testnet=testnet)
tx.locktime = little_endian_to_int(raw[-4:])
else:
tx = Tx.parse(BytesIO(raw), testnet=testnet)
if tx.id() != tx_id: # ๋คํธ์ํฌ์์ ๋ฐ์์จ Transaction๊ณผ ๋ด๊ฐ ์์ฒญํ Transaction์ ๋น๊ต
raise ValueError('not the same id: {} vs {}'.format(tx.id(),
tx_id))
cls.cache[tx_id] = tx
cls.cache[tx_id].testnet = testnet
return cls.cache[tx_id]
@classmethod
def load_cache(cls, filename):
disk_cache = json.loads(open(filename, 'r').read())
for k, raw_hex in disk_cache.items():
raw = bytes.fromhex(raw_hex)
if raw[4] == 0:
raw = raw[:4] + raw[6:]
tx = Tx.parse(BytesIO(raw))
tx.locktime = little_endian_to_int(raw[-4:])
else:
tx = Tx.parse(BytesIO(raw))
cls.cache[k] = tx
@classmethod
def dump_cache(cls, filename):
with open(filename, 'w') as f:
to_dump = {k: tx.serialize().hex() for k, tx in cls.cache.items()}
s = json.dumps(to_dump, sort_keys=True, indent=4)
f.write(s)
class Tx:
...
def fee(self):
'''Returns the fee of this transaction in satoshi'''
# initialize input sum and output sum
input_sum = 0
output_sum = 0
# use TxIn.value() to sum up the input amounts
input_sum = sum([tx_in.value() for tx_in in self.tx_ins])
# use TxOut.amount to sum up the output amounts
output_sum = sum([tx_out.amount for tx_out in self.tx_outs])
# fee is input sum - output sum
fee = input_sum - output_sum
return fee
class TxIn:
...
def fetch_tx(self, testnet=False):
return TxFetcher.fetch(self.prev_tx.hex(), testnet=testnet)
def value(self, testnet=False):
'''Get the output value by looking up the tx hash.
Returns the amount in satoshi.
'''
tx = self.fetch_tx(testnet=testnet)
return tx.tx_outs[self.prev_index].amount
- fetch ๋ฉ์๋๋ ํธ๋์ญ์ ์ id๋ฅผ ๋ฐ์์ ํด๋น ํธ๋์ญ์ ์ ๊ฐ์ ธ์จ๋ค.
- ์ด ๋, TxFetcher ํด๋์ค๋ ์บ์๋ฅผ ์ฌ์ฉํด์ ์ด๋ฏธ ๊ฐ์ ธ์จ ํธ๋์ญ์ ์ ๋ค์ ๊ฐ์ ธ์ค์ง ์๋๋ค.
- ๊ทธ๋ฆฌ๊ณ ๋คํธ์ํฌ์์ ํธ๋์ญ์ ์ ๊ฐ์ ธ์ฌ ๋, ํธ๋์ญ์ ์ id์ ๋คํธ์ํฌ์์ ๊ฐ์ ธ์จ ํธ๋์ญ์ ์ id๊ฐ ๊ฐ์์ง ํ์ธํ๋ค.
- ๋ง์ฝ, ๋คํธ์ํฌ์์ ์์ฒญํ ๊ฒฐ๊ณผ๊ฐ ํธ๋์ญ์ ์ด ์๋๊ณ , ๋ด๊ฐ ์์ฒญํ ์ ๋ ฅ์ ๊ธ์ก์ ๋ฐํ๋ฐ์๋ค๋ฉด,
- ์ 3์๊ฐ ์ ๊ณตํ๋ ์ ๋ณด๋ฅผ โ๊ฒ์ฆโํ ๋ฐฉ๋ฒ์ด ์๋ค.
- ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ํธ๋์ญ์ ์ ๋ณด ์ ์ฒด๋ฅผ ๋ฐ๊ณ , ์ด ํธ๋์ญ์ ๋ด์ฉ์ ๋ํ ํด์๊ฐ์ ํต๊ณผ์์ผ ๊ฒ์ฆํ๋ ๊ณผ์ ์ ์ถ๊ฐํ๋ค๋ฉด,
- ์ ํํ ์ํ๋ ํธ๋์ญ์ ์์ ํ์ธ ๊ฐ๋ฅํ๋ค.