trustwallet / wallet-core

Cross-platform, cross-blockchain wallet library.

Home Page:https://developer.trustwallet.com/wallet-core

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bitcoin AnySigner Error: Unable to Sign Transactions with OP_RETURN Over 80 Bytes

ronaldoguedess opened this issue · comments

Issue Description:

Bitcoin AnySigner encounters an error when attempting to sign a transaction with an OP_RETURN data exceeding 80 bytes. The OP_RETURN data I need for my transaction is 136 bytes long. While reducing it to 80 characters resolves the issue, it doesn't meet my requirements.

Background:

Bitcoin allows OP_RETURN data lengths exceeding 80 bytes, as evidenced by this transaction here, which contains 124 bytes of OP_RETURN data.

Code Snippet:

//...
val opReturnData = "D6037B019c86c912963CBAe893Db3f8DB1a471B7025F0b1a82749cb4E2278EC87F8BF6B618dC71a8bffE26BF9Fe82e05d011f9B2B4Fc6fC9DB6F4d62E400".toByteArray(Charsets.UTF_8)
input.outputOpReturn = ByteString.copyFrom(opReturnData)

//PLAN
val plan = AnySigner.plan(input.build(), CoinType.BITCOIN, Bitcoin.TransactionPlan.parser())
Log.e("xxxxxxxxxxxxx", "Planned fee:  ${plan.fee}  amount: ${plan.amount}  avail_amount: ${plan.availableAmount}  change: ${plan.change}")
input.plan   = plan
input.amount = plan.amount

//SIGN TX
val output = AnySigner.sign(input.build(), CoinType.BITCOIN, Bitcoin.SigningOutput.parser())
val encoded = output.encoded

val signedTransaction = Numeric.toHexString(encoded.toByteArray()).substring(2)
assert(output.error == Common.SigningError.OK)

Expected Behavior:

Bitcoin AnySigner should be able to handle OP_RETURN data exceeding 80 bytes without errors, as per Bitcoin's protocol specifications.

Steps to Reproduce:

  • Create a transaction with an OP_RETURN data length exceeding 80 bytes.
  • Attempt to sign the transaction using Bitcoin AnySigner.
  • Observe the error encountered due to the OP_RETURN data length.

"java.lang.AssertionError: Assertion failed | kotlin.Unit"

Additional Information:

Bitcoin transactions with OP_RETURN data lengths over 80 bytes are valid, and it's possible to check it on this OP_RETURN:
https://blockchair.com/bitcoin/testnet/address/d-449949f1579c08d6e01d45c6bd1e2858

Ensuring compatibility with OP_RETURN data lengths beyond 80 bytes is crucial for various use cases requiring larger data payloads.

Note: This issue is hindering the functionality required for specific transaction types and use cases, necessitating support for OP_RETURN data lengths exceeding 80 bytes in Bitcoin AnySigner.

Hi @ronaldoguedess, the OP_RETURN data limit in the Bitcoin network is currently 80 bytes. This limit is enforced by a consensus rule.
https://www.talkcrypto.org/blog/2016/12/30/op_return-40-to-80-bytes/

I see the only 603c2bd11b048002e00d3527cfa2349f79b5efe724f37a57442030fcdd86caa6 transaction in the d-449949f1579c08d6e01d45c6bd1e2858 address history.
Please note that the mentioned transaction has an OP_RETURN output with 62 pushed bytes, however, its hexadecimal representation equates to 124 bytes.

OMG, you're absolutely right @satoshiotomakan! The issue stemmed from sending UTF8 data. The solution lies in converting it to hex Bytes rather than converting it directly to a UTF8 byte array.

This is how I solve the problem, it could help someone.

fun String.toHexBytes(): ByteArray {
    return Numeric.hexStringToByteArray(this)
}

// OP_RETURN OUTPUT
val opReturnData = "D6037B019c86c912963CBAe893Db3f8DB1a471B7025F0b1a82749cb4E2278EC87F8BF6B618dC71a8bffE26BF9Fe82e05d011f9B2B4Fc6fC9DB6F4d62E400".toHexBytes()
input.outputOpReturn = ByteString.copyFrom(opReturnData)

Using UTF-8 encoding, an Ethereum address is considered to be 40 bytes, whereas using hexadecimal encoding reduces it to just 20 bytes. Quite a significant difference!

I hope it helps someone. Bye.