我正在开发一个与私有以太坊区块链网络交互的REST服务。首先,我将Java与Web3j库和Jersey一起使用。一切都按预期工作,但服务的单个实例(不是 geth 客户端(占用了高达 500MB 的 RAM!由于我需要同时运行多个实例(~40(以进行模拟,因此我想要更轻的实例。
因此,我切换到Go(32位版本,Windows 10(和原始的go-ethereum软件包。但是,我非常惊讶地发现,一旦我调用并存储bind.NewTransactor()
的结果,该程序的内存消耗就会上升到~250MB,这将返回一个*TransactOpts
。我看了一下消息来源,但我无法解释这种行为。
这是正常的(如果是,为什么?(还是我错过了什么?
这是我的代码:
import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"strings"
)
const WALLET_PATH string = "path/to/json/wallet"
func main() {
data, _ := ioutil.ReadFile(WALLET_PATH)
// up to this point, the program only takes a few MBs of RAM
auth, _ := bind.NewTransactor(strings.NewReader(string(data)), "mypassphrase")
// and here the allocated memory rises up to ~270MB
_ = auth
}
好吧,我并没有真正找到问题的答案,但是由于我确实找到了解决方法,因此无论如何我都会分享它。
内存分析
正如@MichaelHampton建议的那样,我记忆了我的程序。top 10
命令揭示了以下内容:
(pprof) top 10
Showing nodes accounting for 256MB, 99.88% of 256.30MB total
Dropped 15 nodes (cum <= 1.28MB)
flat flat% sum% cum cum%
256MB 99.88% 99.88% 256MB 99.88% github.com/ethereum/go-ethereum/vendor/golang.org/x/crypto/scrypt.Key
0 0% 99.88% 256MB 99.88% github.com/ethereum/go-ethereum/accounts/abi/bind.NewTransactor
0 0% 99.88% 256MB 99.88% github.com/ethereum/go-ethereum/accounts/keystore.DecryptKey
0 0% 99.88% 256MB 99.88% github.com/ethereum/go-ethereum/accounts/keystore.decryptKeyV3
0 0% 99.88% 256MB 99.88% github.com/ethereum/go-ethereum/accounts/keystore.getKDFKey
0 0% 99.88% 256.01MB 99.88% main.main
0 0% 99.88% 256.30MB 100% runtime.main
如您所见,内存消耗来自包scrypt
中的Key
函数,该函数由bind.NewTransactor(...)
间接调用。从文档中:
从密码、盐和成本参数派生密钥,返回可用作加密密钥的长度 keyLen 的字节片。
特别是,这对应于从 JSON 钱包文件生成私钥的方式。实际上,该函数本身为加密计算分配了大量内存。我不明白的是,为什么一旦生成密钥,这种巨大的内存分配似乎仍然存在(当我在之后以log.Fatal(http.ListenAndServe(":8080", nil))
启动服务器时,会观察到持久性(。
解决方法
为了证实我的怀疑,我使用了另一种方法来生成我的*TransactOpts
,其中包括直接从其十六进制表示形式获取密钥,而不是从钱包文件生成它:
func main() {
privateKey, err := crypto.HexToECDSA("myKeyInHex")
if err != nil {
log.Fatal(err)
}
auth := bind.NewKeyedTransactor(privateKey)
_ = auth
}
尽管bind.NewTransactor
和bind.NewKeyedTransactor
返回完全相同的对象(唯一的区别是密钥的生成方式(,但使用bind.NewTransactor
会导致 256MB 的持久内存分配,而不是用于bind.NewKeyedTransactor
的几 KB,如下所示:
(pprof) top 10
Showing nodes accounting for 11.04kB, 100% of 11.04kB total
Showing top 10 nodes out of 19
flat flat% sum% cum cum%
6.83kB 61.90% 61.90% 6.83kB 61.90% time.initLocalFromTZI
4.21kB 38.10% 100% 4.21kB 38.10% github.com/ethereum/go-ethereum/crypto/sha3.(*state).clone (inline)
0 0% 100% 4.21kB 38.10% github.com/ethereum/go-ethereum/accounts/abi/bind.NewKeyedTransactor
0 0% 100% 4.21kB 38.10% github.com/ethereum/go-ethereum/crypto.Keccak256
0 0% 100% 4.21kB 38.10% github.com/ethereum/go-ethereum/crypto.PubkeyToAddress
0 0% 100% 4.21kB 38.10% github.com/ethereum/go-ethereum/crypto/sha3.(*state).Sum
0 0% 100% 6.83kB 61.90% github.com/pkg/profile.Start
0 0% 100% 6.83kB 61.90% github.com/pkg/profile.Start.func2
0 0% 100% 6.83kB 61.90% log.(*Logger).Output
0 0% 100% 6.83kB 61.90% log.(*Logger).formatHeader
因此,对于我的模拟,我将从我的 JSON 钱包生成我的私钥并将它们存储在上游的文本文件中,然后使用bind.NewKeyedTransactor(...)
.我知道这无论如何都不安全,但对于我的模拟目的来说,这就足够了。
但是,我很确定bind.NewTransactor
没有有关内存消耗的预期行为,因此我将在go-ethereum
存储库上打开一个问题。