bad-txns-token-insufficient

発行したトランザクションが bad-txns-token-insufficient とのエラーを吐いてブロードキャスト出来ない問題。

2022-04-12T13:08:08Z [default wallet] CommitTransaction(): Transaction cannot be broadcast immediately, bad-txns-token-insufficient (code 66)

メモリプールの検証テストでもエラーを確認できる。

$ tapyrus-cli testmempoolaccept '["010000000100f5ae6e3e16751c523bdb8eb07a45ced16c9cd524740010d97d02b9afb5493b010000006a4730440220535b78168ea744b4a2cd3829d9e2b04ada585d71618422b5a669bfa3baaa7a61022060743d1cf2b3627dd52dc9c870b841697ee2c93909966a9ffdbd4f6c412231d901210229f9333478d5e9a3c5e3a451383bfd27f124b4891e97af066060ca963a5acabdfeffffff0201000000000000003a21c315e9dced5401fe0ae1caaec69ea382440b0dfdbac7803495b8d58f92fc7d62acbca91408d01c28e28e00f0d59ef05b84df8fb6445e87e387b5ddf505000000001976a9149a101bc5c0daf28b73c4697a7eff19e131eca06a88ac94dd0200"]' [ { "txid": "d194e9097320090b6f8d4a13c1a0f383f959a2c1d6b05b0d2812019f2ed12caa", "allowed": false, "reject-reason": "66: bad-txns-token-insufficient" } ]

ちなみに対象トランザクションはissuetokenの出力。

先に結論

fallbackfee が足りなかった。

自分の環境では 0.0002 で通りました。

bad-txns-token-insufficient

mempoolaccept(メモリプールでのトランザクションの検証)の際に起こるエラー。内容は「トランザクションを直ちにブロードキャストすることができません。」

ソースコード内での呼び出し箇所は以下。

//if TPC input is sufficiently large this is a token issue. if(tpcin < 0 || tpcin - tpcout - ::minRelayTxFee.GetFee(nSize) < 1) return state.Invalid(false, REJECT_INSUFFICIENTFEE, "bad-txns-token-insufficient");
https://github.com/chaintope/tapyrus-core/blob/95fd0156e5012a9833a0d2cb5faed4363618bf1f/src/validation.cpp#L1049

コメントでは「もしインプットのTPCが十分に大きい場合、これはトークンの問題です」と言われている。

変数について

  • tpcin = たぶんインプットのTPC
  • tpcout = たぶんアウトプットのTPC
  • nSize = entry.GetTxSize() たぶんトランザクションのサイズ
  • ::minRelayTxFee.GetFee(nSize) = 該当トランザクションサイズにおけるメモリプールの最低手数料
    • minRelayTxFee: メモリプールにおける最低手数料。ただし絶対的な料金ではなく、fee rateである。Bitcoinにおけるデフォルト値は1000 satoshi/kB (= 1 sat/B)。これは -minrelaytxfee で指定できる。また次のコマンドで現在の値を取得することができる。 - https://bitcoin.stackexchange.com/questions/48235/what-is-the-minrelaytxfee
    • .GetFee(nSize): minRelayTxFee はfee rateなのでここで実際の手数料値を取得する。

つまり if 文は「もしインプットのTPCが0未満なら、またはインプット-アウトプット-最低手数料が1未満なら」

トランザクションサイズについては decoderawtransaction で取得可能。

tapyrus-cli -conf=/etc/tapyrus/tapyrus.conf decoderawtransaction 0100000001b35b003d6c527b66667e7c171587cf8ae6a5846aa712f883f6f7ff11691122f7000000006a47304402203ad88b98eb89dd45fb61c2fac576ca0f1f6508ba486c0379c6d129d627181880022049bf5671360b6d63581aedfc8d737557fcd764a62afb2e0c6744de4f712a731e0121035a43bc4920e5a3dbd2304fc03d7f0148062f57b5b4f9f77582dd3f62ba7ab87ffeffffff020a000000000000003a21c27a34d9f3602badf8110d65ee06cc7647539e885c29a981d02b6ee36daf84e4ecbca9147c1be1708579ff311e927574c6fcbe0c18cb01b0877e959800000000001976a9143c26a4e63c9bf74385567c18add2053b2ebe0e9788ac87de0200 { "txid": "2065d271630df4dabc3d77e814006cc0f141c12020365586a7b5332a18bd9e06", "hash": "5fec95042c098e9e306f59278fcf8fa7f67c1c40c23843a7bf141b39a2bc8f10", "features": 1, "size": 258,

また minRelayTxFee もgetmempoolinfo で取得できる。

$ tapyrus-cli -conf=/etc/tapyrus/tapyrus.conf getmempoolinfo { "size": 0, "bytes": 0, "usage": 0, "maxmempool": 300000000, "mempoolminfee": 0.00001000, "minrelaytxfee": 0.00001000 }

GetFeeの処理から手数料を計算できそう。

CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nBytes_) { assert(nBytes_ <= uint64_t(std::numeric_limits<int64_t>::max())); int64_t nSize = int64_t(nBytes_); if (nSize > 0) nTapyrusPerk = nFeePaid * 1000 / nSize; else nTapyrusPerk = 0; } CAmount CFeeRate::GetFee(size_t nBytes_) const { assert(nBytes_ <= uint64_t(std::numeric_limits<int64_t>::max())); int64_t nSize = int64_t(nBytes_); CAmount nFee = nTapyrusPerk * nSize / 1000; if (nFee == 0 && nSize != 0) { if (nTapyrusPerk > 0) nFee = CAmount(1); if (nTapyrusPerk < 0) nFee = CAmount(-1); } return nFee; }

ここまでの情報から、勘で計算(0.00001 x 258 / 1000)すると最低手数料は 0.00000258 。だからどうした・オブ・ザ・イヤー。

なんにせよ、おそらく手数料が足りていないことは確か。インプットからアウトプットを引いた値は払う手数料に等しいはずで(tpcin - tpcout)、それと最低手数料の差額が 1 以下であると当該のエラーを吐いているはず。

実際に払ってそうな手数料は gettransaction でウォレット内トランザクションの情報を参照すると分かる。

$ tapyrus-cli gettransaction d194e9097320090b6f8d4a13c1a0f383f959a2c1d6b05b0d2812019f2ed12caa { "token": "TPC", "amount": 0.00000000, "fee": -0.00000258, "confirmations": 0, ...

0.00000258 。「たぶん」で計算した最低手数料が 0.00000258 。確かに 1 未満なので if文を満たす。なるほどね...。

最低手数料満たしたのにエラーなの...???🥺  と泣いていたところ、fallbackfeeとやらの存在を思い出したので、これまた勘で tapyrus.conf のfallbackfee をBitcoinのデフォルトらしい 0.0002 に設定して再度発行してみると(以前は 0.000001

以下の警告は出たが通った!!!

2022-04-13T12:37:08Z [default wallet] Fee Calculation: Fee:5160 Bytes:258 Needed:5160 Tgt:0 (requested 6) Reason:"Fallback fee" Decay 0.00000: Estimation: (-1 - -1) -nan% 0.0/(0.0 0 mem 0.0 out) Fail: (-1 - -1) -nan% 0.0/(0.0 0 mem 0.0 out)

listunspentでもトークンの発行を確認できる。

$ tapyrus-cli -conf=/etc/tapyrus/tapyrus.conf listunspent ... { "txid": "79f063fde1003bc2f55385aa4c68017719085f0ab06d0ab2a21bef3242409b1e", "vout": 0, "address": "4Zy3DGMko5KfTEtFRzyp2dpdL8LabKnrofCPoinDU7J2CaUbYLMYm6TtD5AVY9yUSstsWga8MQ3wuzm", "token": "c37a34d9f3602badf8110d65ee06cc7647539e885c29a981d02b6ee36daf84e4ec", "amount": 1, "scriptPubKey": "21c37a34d9f3602badf8110d65ee06cc7647539e885c29a981d02b6ee36daf84e4ecbca914947202d69e951c8a6321ee13b53aeac9bc07fd4087", "confirmations": 1, "spendable": true, "solvable": true, "safe": true }, { "txid": "79f063fde1003bc2f55385aa4c68017719085f0ab06d0ab2a21bef3242409b1e", "vout": 1, "address": "1KKHRLnEUqJvBQ5vJvmdBAyajZn9cAoZVy", "token": "TPC", "amount": 0.09994840, "scriptPubKey": "76a914c8e77537295405895d18ca4f08d2638c29b4219888ac", "confirmations": 1, "spendable": true, "solvable": true, "safe": true } ]

成功時の手数料はどうやら 0.00005160 らしい。

$ tapyrus-cli -conf=/etc/tapyrus/tapyrus.conf gettransaction 79f063fde1003bc2f55385aa4c68017719085f0ab06d0ab2a21bef3242409b1e { "token": "TPC", "amount": 0.00000000, "fee": -0.00005160, "confirmations": 14, ...
結論

fallbackfee が足りなかった。

そもそもfallbackfeeとはなんだ
A fee rate (in BTC/kB) that will be used when fee estimation has insufficient data (default: 0.0002) - https://manpages.debian.org/experimental/bitcoin-qt/bitcoin-qt.1.en.html
Sometimes, it is not possible to give good estimates, or an estimate at all. Therefore, a fallback value can be set with fallbackfee=<f> (default: 0.0002 BTC/kB). - https://en.bitcoin.it/wiki/Miner_fees

適切な見積もりを出すことができないか、まったく見積もり出来なかった際に使用されるfee rate。

ちなみにBitcoinの testnet, regnet では 0.0002 がデフォルト値だった。v0.20.0からは 0 に変更された。mainnetはずっと 0

Tapyrusで 0 とすると以下のエラーが出る。

error code: -4 error message: Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.

0 にはさせてくれなさそう。

おまけ

デバッグしまくったせいで成功トランザクションがウォレット内の無効トランザクションと衝突した。

abandontransactionしたけどどうやらまだ残ってそう。

2022-04-13T12:40:43Z [default wallet] Transaction 79f063fde1003bc2f55385aa4c68017719085f0ab06d0ab2a21bef3242409b1e (in block 1ca95cc5c24d7c90fc7f91d2accb6342e862839f3dc528291f94cd37bc5032d7) conflicts with wallet transaction 2065d271630df4dabc3d77e814006cc0f141c12020365586a7b5332a18bd9e06 (both spend f722116911fff7f683f812a76a84a5e68acf8715177c7e66667b526c3d005bb3:0) 2022-04-13T12:40:43Z [default wallet] Transaction 79f063fde1003bc2f55385aa4c68017719085f0ab06d0ab2a21bef3242409b1e (in block 1ca95cc5c24d7c90fc7f91d2accb6342e862839f3dc528291f94cd37bc5032d7) conflicts with wallet transaction 99f4819aec212c7656d91dfcecc0322fc70b096ec986c718908635c0357e6109 (both spend f722116911fff7f683f812a76a84a5e68acf8715177c7e66667b526c3d005bb3:0) 2022-04-13T12:40:43Z [default wallet] Transaction 79f063fde1003bc2f55385aa4c68017719085f0ab06d0ab2a21bef3242409b1e (in block 1ca95cc5c24d7c90fc7f91d2accb6342e862839f3dc528291f94cd37bc5032d7) conflicts with wallet transaction 55c25121232dd010b9433d7e70506e42a07d805bb9a49abf4cfcb09fe3ad8fd6 (both spend f722116911fff7f683f812a76a84a5e68acf8715177c7e66667b526c3d005bb3:0)

ところでテストネットへ接続して初めてトークンを作った時は成功したのなぜだったんだろう...