使用eos-go交易过期问题修复
转载请注明:https://www.cnblogs.com/tkblack/p/12659826.html
前段时间,使用eos-go发交易进行测试,程序在我本机运行时是OK的,但是放到服务器运行就报错,提示错误: Internal Service Error: Expired Transaction。
看到这个报错提示,很明显的知道,交易过期了,也就是我们设置的交易过期时间小于当前时间。我看了下代码,实现如下(注意红色部分):
func (tx *Transaction) SetExpiration(in time.Duration) { tx.Expiration = JSONTime{time.Now().UTC().Add(in)} }
具体设置过期时间如下红色部分:
func (tx *Transaction) Fill(headBlockID Checksum256, delaySecs, maxNetUsageWords uint32, maxCPUUsageMS uint8) { tx.setRefBlock(headBlockID) if tx.ContextFreeActions == nil { tx.ContextFreeActions = make([]*Action, 0, 0) } if tx.Extensions == nil { tx.Extensions = make([]*Extension, 0, 0) } tx.MaxNetUsageWords = Varuint32(maxNetUsageWords) tx.MaxCPUUsageMS = maxCPUUsageMS tx.DelaySec = Varuint32(delaySecs) tx.SetExpiration(30 * time.Second) }
显然,过期时间在本地时间的基础上增加30秒,理论30秒足够了,但是程序中使用的是本地时间,因此,如果本地时间滞后超过30秒,就会出现过期错误。
解决这个问题也挺简单的,如果不想改代码,那直接调整本地时间就好了;当然也可以将过期时间设置长一点,比如设置为加300秒。
当然,这里还有一个方法,设置过期时间为链上返回的时间加30秒,也就是不用time.Now(),这样就不会受本地时间的影响而导致出现交易过期的错误。修改代码也比较简单:
(仅需修改transaction.go文件中的代码,绿色表示原来的代码,红色表示添加或修改的代码)
// transaction.go
//......(省略)
// NewTransaction creates a transaction. Unless you plan on adding HeadBlockID later, to be complete, opts should contain it. Sign func NewTransaction(actions []*Action, opts *TxOptions) *Transaction { if opts == nil { opts = &TxOptions{} } tx := &Transaction{Actions: actions}
/* modify by tkblack begin */ //tx.Fill(opts.HeadBlockID, opts.DelaySecs, opts.MaxNetUsageWords, opts.MaxCPUUsageMS) tx.Fill(opts.HeadBlockID, opts.DelaySecs, opts.MaxNetUsageWords, opts.MaxCPUUsageMS, opts.Expiration) /* modify by tkblack end */
return tx }
//......(省略)
func (tx *Transaction) Fill(headBlockID Checksum256, delaySecs, maxNetUsageWords uint32, maxCPUUsageMS uint8, expiration JSONTime) {
tx.setRefBlock(headBlockID)
if tx.ContextFreeActions == nil {
tx.ContextFreeActions = make([]*Action, 0, 0)
}
if tx.Extensions == nil {
tx.Extensions = make([]*Extension, 0, 0)
}
tx.MaxNetUsageWords = Varuint32(maxNetUsageWords)
tx.MaxCPUUsageMS = maxCPUUsageMS
tx.DelaySec = Varuint32(delaySecs)
/* modify by tkblack begin */
//tx.SetExpiration(30 * time.Second)
//expiration.Time = expiration.Time.Add(30 * time.Second)
tx.Expiration = expiration
/* modify by tkblack end */
}
//......(省略)
type TxOptions struct {
ChainID Checksum256 // If specified, we won't hit the API to fetch it
HeadBlockID Checksum256 // If provided, don't hit API to fetch it. This allows offline transaction signing.
//MaxNetUsageWords uint32
DelaySecs uint32
MaxCPUUsageMS uint8 // If you want to override the CPU usage (in counts of 1024)
//ExtraKCPUUsage uint32 // If you want to *add* some CPU usage to the estimated amount (in counts of 1024)
Compress CompressionType
/* modify by tkblack begin */
//
Expiration JSONTime
/* modify by tkblack end */
}
//......(省略)
// FillFromChain will load ChainID (for signing transactions) and // HeadBlockID (to fill transaction with TaPoS data). func (opts *TxOptions) FillFromChain(api *API) error { if opts == nil { return errors.New("TxOptions should not be nil, send an object") } if opts.HeadBlockID == nil || opts.ChainID == nil { info, err := api.cachedGetInfo() if err != nil { return err } if opts.HeadBlockID == nil { opts.HeadBlockID = info.HeadBlockID } if opts.ChainID == nil { opts.ChainID = info.ChainID }
/* modify by tkblack begin */ // opts.Expiration.Time = info.HeadBlockTime.Add(30 * time.Second) /* modify by tkblack end */ } return nil }
以上原理即通过get_info命令获取到最新区块的时间戳,然后加30秒作为过期时间,因此过期时间始终比链上时间相多30秒,至于本地时间是否准确不会影响程序运行。