Why signed tx should be encoded to bytes before sending raw transaction?

huangapple go评论109阅读模式
英文:

Why signed tx should be encoded to bytes before sending raw transaction?

问题

我想用合约调用的方式写入以太坊区块链。我已经找到了两种几乎相同的解决方案,但其中一种在发送之前对签名交易进行了一些字节编码操作,我无法弄清楚为什么要这样做。我的问题是,为什么 解决方案 #2解决方案 #3 相比 解决方案 #1 使用了额外的行?额外的字节操作部分的目的是什么?signedTxtxToSend 都是 *types.Transaction 类型,我不明白为什么需要进行编码操作。go-ethereum 包的文档中说明:

> SendTransaction 将已签名的交易注入待处理池以供执行。

它没有提供关于交易的进一步信息,而 types.SignTx() 返回的是 *types.Transaction 类型。

解决方案 #1

这是最简单的解决方案,不对 signedTx 进行任何操作。

tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
	log.Fatal(err)
}
txErr := client.SendTransaction(context.Background(), tx)
if txErr != nil {
	log.Fatalf("调用合约时出错:%v", err)
}

解决方案 #2

这是 Go Ethereum Book 的创建原始交易发送原始交易部分中使用的实现方式。

tx_signed := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)

signedTx, err := types.SignTx(tx_signed, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
  log.Fatal(err)
}

ts := types.Transactions{signedTx}
rawTxBytes := ts.GetRlp(0)
rawTxHex := hex.EncodeToString(rawTxBytes)
rawBytes, err := hex.DecodeString(rawTxHex)

tx := new(types.Transaction)
rlp.DecodeBytes(rawBytes, &tx)

txErr := client.SendTransaction(context.Background(), tx)
if txErr != nil {
	log.Fatalf("调用合约时出错:%v", err)
}

解决方案 #3

这个实现与前面的实现几乎相同,但它使用了较新的 EncodeIndex(i int, w *bytes.Buffer) 函数进行字节操作。来源

tx_signed := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)

signedTx, err := types.SignTx(tx_signed, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
  log.Fatal(err)
}

ts := types.Transactions{signedTx}
b := new(bytes.Buffer)
ts.EncodeIndex(0, b)
rawTxBytes := b.Bytes()

txToSend := new(types.Transaction)
rlp.DecodeBytes(rawTxBytes, &txToSend)
txErr := client.SendTransaction(context.Background(), tx)
if txErr != nil {
	log.Fatalf("调用合约时出错:%v", err)
}
英文:

I would like to write the Ethereum blockchain with a contract call. I already found two solutions which are almost the same, but one of them is manipulating the signed transaction, doing some byte encodings before sending it and I couldn't figure out why. My question is that why solution #2 and solution #3 uses the extra lines compared to solution #1? What is the purpose of the extra byte manipulation part? signedTx and txToSend are both *types.Transaction types, I don't understand why is it needed to do the encodings. The documentation of the go-ethereum package states that:

> SendTransaction injects a signed transaction into the pending pool for
> execution.

It doesn't give further information about the tx and types.SignTx() returns *types.Transaction type.

Solution #1

This is the simplest solution without doing any manipulation with signedTx.

tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
	log.Fatal(err)
}
txErr := client.SendTransaction(context.Background(), tx)
if txErr != nil {
	log.Fatalf("Error calling contract: %v", err)
}

Solution #2

This is the implementation used by the Go Ethereum Book's creating raw transaction and sending raw transaction part.

tx_signed := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)

signedTx, err := types.SignTx(tx_signed, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
  log.Fatal(err)
}

ts := types.Transactions{signedTx}
rawTxBytes := ts.GetRlp(0)
rawTxHex := hex.EncodeToString(rawTxBytes)
rawBytes, err := hex.DecodeString(rawTxHex)

tx := new(types.Transaction)
rlp.DecodeBytes(rawBytes, &tx)

txErr := client.SendTransaction(context.Background(), tx)
if txErr != nil {
	log.Fatalf("Error calling contract: %v", err)
}

Solution #3

This implementation is almost the same as the previous, but it uses the newer EncodeIndex(i int, w *bytes.Buffer) function for the byte manipulation. Source

tx_signed := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)

signedTx, err := types.SignTx(tx_signed, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
  log.Fatal(err)
}

ts := types.Transactions{signedTx}
b := new(bytes.Buffer)
ts.EncodeIndex(0, b)
rawTxBytes := b.Bytes()

txToSend := new(types.Transaction)
rlp.DecodeBytes(rawTxBytes, &txToSend)
txErr := client.SendTransaction(context.Background(), tx)
if txErr != nil {
	log.Fatalf("Error calling contract: %v", err)
}

答案1

得分: 1

以太坊交易有不同的类型:EIP1559、EIP2711、EIP2718。

并非所有的geth客户端都支持每种交易类型。我认为你提到的编码在问题中的交易是EIP2718。

从上面的文章中可以得知:

EIP-2718定义了一种新的通用封装方式来处理带类型的交易。在新的标准中,交易的格式如下:

TransactionType || TransactionPayload

其中字段的定义如下:

TransactionType:一个介于0和0x7f之间的数字,总共有128种可能的交易类型。
TransactionPayload:由交易类型定义的任意字节数组。这些字段被连接(组合)起来形成一个带类型的交易。该标准没有描述交易负载的格式;它可以是任意一系列字节,使用新的交易类型定义的任何编码器进行编码(例如RLP、SSZ等)。选择简单的字节连接是因为可以轻松地读取字节数组的第一个字节,无需任何库或工具:您不需要RLP或SSZ解析器来检查交易类型。

或者可能是其他类型的EIP。请检查你的geth客户端版本,以及支持哪些类型。

英文:

There are different types of ethereum transactions: EIP1559, EIP2711, EIP2718

Not all geth clients supports each transaction type. I think the transactions that encoded in your question are EIP2718.

https://blog.mycrypto.com/new-transaction-types-on-ethereum

From the above article:

> EIP-2718 defines a new generalised envelope for typed transactions. In
> the new standard, transactions look like this:
>
> TransactionType || TransactionPayload
>
> Where the fields are defined as:
>
> TransactionType: a number between 0 and 0x7f, for a total of 128
> possible transaction types. TransactionPayload: an arbitrary byte
> array, defined by the transaction type. These fields are concatenated
> (combined) to form a typed transaction. The standard does not describe
> a format for the transaction payload; it can be any arbitrary series
> of bytes, encoded with any encoder as defined by the new transaction
> type (e.g., RLP, SSZ, …). Simple byte concatenation was chosen because
> it's trivial to read the first byte of a byte array without the need
> for any libraries or tools: You don't need an RLP or SSZ parser to
> check the transaction type.

or maybe a different EIP type. check your geth client version, and which ones that support

答案2

得分: 0

如果目标是创建一个交易并广播它,解决方案1是可以的。在幕后,golang库将交易编码为字节数组并将其发送到geth节点。

我认为这本书想要更精确地展示中间发生了什么(例如打印编码交易的十六进制)。这就是为什么他进行编码的原因。这是强制性的吗?不是的。

能够编码和解码交易可能是有用的,例如你在其他地方创建了交易,只想在你的go程序中签名/广播它。在这种情况下,你必须解码字节数组并从中创建交易。

英文:

If the goal is to create a transaction and broadcast it, solution 1 is fine. Behind the scene, the golang library encodes the transaction to a byte array and sends it to the geth node.

I think the book wanted to be more precise and show what happens in the middle (e.g. print the hex of the encoded transaction). This is the reason why he does that encoding. Is it mandatory? No.

It could be useful to be able to encode and decode transaction, for example you create the transaction somewhere else and you want only to sign/broadcast it inside your go program. In this case you have to decode the array of bytes and create the transaction from it.

huangapple
  • 本文由 发表于 2022年8月21日 06:08:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/73430412.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定