Merge pull request #2941 from nspcc-dev/drop-deprecated-0.102.0

Drop some deprecated things in 0.102.0
This commit is contained in:
Roman Khimov 2023-04-06 10:42:26 +03:00 committed by GitHub
commit e2cf5b868a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 43 additions and 138 deletions

View file

@ -40,17 +40,6 @@ While a lot of the code is already converted to new APIs, old ones still can
be used in some code not known to us. Therefore we will remove old APIs not be used in some code not known to us. Therefore we will remove old APIs not
earlier than May 2023, with 0.103.0 release. earlier than May 2023, with 0.103.0 release.
## util.FromAddress smart contract helper
`util` smart contract library has a FromAddress function that is one of the
oldest lines in the entire NeoGo code base, dating back to 2018. Version
0.99.4 of NeoGo (October 2022) has introduced a new `address` package with
`ToHash160` function, it covers a bit more use cases but can be used as a
direct replacement of the old function, so please update your code.
util.FromAddress is expected to be removed around March 2023 (~0.102.0
release).
## WSClient Notifications channel and SubscribeFor* APIs ## WSClient Notifications channel and SubscribeFor* APIs
Version 0.99.5 of NeoGo introduces a new set of subscription APIs that gives Version 0.99.5 of NeoGo introduces a new set of subscription APIs that gives
@ -61,15 +50,6 @@ Receive* APIs.
Removal of these APIs is scheduled for May 2023 (~0.103.0 release). Removal of these APIs is scheduled for May 2023 (~0.103.0 release).
## Prometheus RPC counters
A number of neogo_${method}_called Prometheus counters are marked as
deprecated since version 0.99.5, neogo_rpc_${method}_time histograms can be
used instead (that also have a counter).
It's not a frequently used thing and it's easy to replace it, so removal of
old counters is scheduled for January-February 2023 (~0.100.X release).
## SecondsPerBlock protocol configuration ## SecondsPerBlock protocol configuration
With 0.100.0 version SecondsPerBlock protocol configuration setting was With 0.100.0 version SecondsPerBlock protocol configuration setting was

View file

@ -432,10 +432,10 @@ go 1.18`)
srcCheckWitness := `package kek srcCheckWitness := `package kek
import ( import (
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/util" "github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
) )
func Main() bool { func Main() bool {
var owner = util.FromAddress("` + ownerAddress + `") var owner = address.ToHash160("` + ownerAddress + `")
return runtime.CheckWitness(owner) return runtime.CheckWitness(owner)
} }
` `

View file

@ -10,6 +10,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract" "github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
"github.com/nspcc-dev/neo-go/pkg/interop/native/crypto" "github.com/nspcc-dev/neo-go/pkg/interop/native/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas" "github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
@ -39,7 +40,7 @@ var (
// contractOwner is a special address that can perform some management // contractOwner is a special address that can perform some management
// functions on this contract like updating/destroying it and can also // functions on this contract like updating/destroying it and can also
// be used for contract address verification. // be used for contract address verification.
contractOwner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") contractOwner = address.ToHash160("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB")
) )
// ObjectIdentifier represents NFT structure and contains the container ID and // ObjectIdentifier represents NFT structure and contains the container ID and

View file

@ -11,6 +11,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract" "github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
"github.com/nspcc-dev/neo-go/pkg/interop/native/crypto" "github.com/nspcc-dev/neo-go/pkg/interop/native/crypto"
"github.com/nspcc-dev/neo-go/pkg/interop/native/gas" "github.com/nspcc-dev/neo-go/pkg/interop/native/gas"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
@ -35,7 +36,7 @@ var (
// contractOwner is a special address that can perform some management // contractOwner is a special address that can perform some management
// functions on this contract like updating/destroying it and can also // functions on this contract like updating/destroying it and can also
// be used for contract address verification. // be used for contract address verification.
contractOwner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") contractOwner = address.ToHash160("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB")
) )
// Symbol returns token symbol, it's HASHY. // Symbol returns token symbol, it's HASHY.

View file

@ -1,14 +1,14 @@
package runtimecontract package runtimecontract
import ( import (
"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
) )
var ( var (
// Check if the invoker of the contract is the specified owner // Check if the invoker of the contract is the specified owner
owner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") owner = address.ToHash160("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB")
) )
// init is transformed into _initialize method that is called whenever contract // init is transformed into _initialize method that is called whenever contract

View file

@ -3,10 +3,10 @@ package timer
import ( import (
"github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract" "github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std" "github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
) )
const defaultTicks = 3 const defaultTicks = 3
@ -16,7 +16,7 @@ var (
// ctx holds storage context for contract methods // ctx holds storage context for contract methods
ctx storage.Context ctx storage.Context
// Check if the invoker of the contract is the specified owner // Check if the invoker of the contract is the specified owner
owner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") owner = address.ToHash160("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB")
// ticksKey is a storage key for ticks counter // ticksKey is a storage key for ticks counter
ticksKey = []byte("ticks") ticksKey = []byte("ticks")
) )

View file

@ -3,8 +3,8 @@ package tokencontract
import ( import (
"github.com/nspcc-dev/neo-go/examples/token/nep17" "github.com/nspcc-dev/neo-go/examples/token/nep17"
"github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
) )
const ( const (
@ -13,7 +13,7 @@ const (
) )
var ( var (
owner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") owner = address.ToHash160("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB")
token nep17.Token token nep17.Token
ctx storage.Context ctx storage.Context
) )

View file

@ -3,12 +3,12 @@ package testdata
import ( import (
"github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/contract" "github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
"github.com/nspcc-dev/neo-go/pkg/interop/native/management" "github.com/nspcc-dev/neo-go/pkg/interop/native/management"
"github.com/nspcc-dev/neo-go/pkg/interop/native/neo" "github.com/nspcc-dev/neo-go/pkg/interop/native/neo"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/storage"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
) )
const ( const (
@ -16,7 +16,7 @@ const (
decimals = 2 decimals = 2
) )
var owner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB") var owner = address.ToHash160("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB")
func Init() bool { func Init() bool {
ctx := storage.GetContext() ctx := storage.GetContext()

View file

@ -1,6 +1,7 @@
package verify package verify
import ( import (
"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/interop/util" "github.com/nspcc-dev/neo-go/pkg/interop/util"
) )
@ -9,6 +10,6 @@ import (
// It returns true iff it is signed by Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn (id-0 private key from testchain). // It returns true iff it is signed by Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn (id-0 private key from testchain).
func Verify() bool { func Verify() bool {
tx := runtime.GetScriptContainer() tx := runtime.GetScriptContainer()
addr := util.FromAddress("Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn") addr := address.ToHash160("Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn")
return util.Equals(string(tx.Sender), string(addr)) return util.Equals(string(tx.Sender), string(addr))
} }

View file

@ -24,10 +24,6 @@ var (
var ( var (
// Go language builtin functions. // Go language builtin functions.
goBuiltins = []string{"len", "append", "panic", "make", "copy", "recover", "delete"} goBuiltins = []string{"len", "append", "panic", "make", "copy", "recover", "delete"}
// Custom builtin utility functions.
customBuiltins = []string{
"FromAddress",
}
// Custom builtin utility functions that contain some meaningful code inside and // Custom builtin utility functions that contain some meaningful code inside and
// require code generation using standard rules, but sometimes (depending on // require code generation using standard rules, but sometimes (depending on
// the expression usage condition) may be optimized at compile time. // the expression usage condition) may be optimized at compile time.
@ -642,18 +638,6 @@ func isGoBuiltin(name string) bool {
return false return false
} }
func isCustomBuiltin(f *funcScope) bool {
if !isInteropPath(f.pkg.Path()) {
return false
}
for _, n := range customBuiltins {
if f.name == n {
return true
}
}
return false
}
func isPotentialCustomBuiltin(f *funcScope, expr ast.Expr) bool { func isPotentialCustomBuiltin(f *funcScope, expr ast.Expr) bool {
if !isInteropPath(f.pkg.Path()) { if !isInteropPath(f.pkg.Path()) {
return false return false

View file

@ -458,10 +458,10 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl, pkg *types.
} else { } else {
f, ok = c.funcs[c.getFuncNameFromDecl("", decl)] f, ok = c.funcs[c.getFuncNameFromDecl("", decl)]
if ok { if ok {
// If this function is a syscall or builtin we will not convert it to bytecode. // If this function is a syscall we will not convert it to bytecode.
// If it's a potential custom builtin then it needs more specific usages research, // If it's a potential custom builtin then it needs more specific usages research,
// thus let's emit the code for it. // thus let's emit the code for it.
if isSyscall(f) || isCustomBuiltin(f) { if isSyscall(f) {
return f return f
} }
c.setLabel(f.label) c.setLabel(f.label)
@ -978,7 +978,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
f, ok = c.funcs[name] f, ok = c.funcs[name]
if ok { if ok {
f.selector = fun.X f.selector = fun.X
isBuiltin = isCustomBuiltin(f) || isPotentialCustomBuiltin(f, n) isBuiltin = isPotentialCustomBuiltin(f, n)
if canInline(f.pkg.Path(), f.decl.Name.Name, isBuiltin) { if canInline(f.pkg.Path(), f.decl.Name.Name, isBuiltin) {
c.inlineCall(f, n) c.inlineCall(f, n)
return nil return nil
@ -1926,7 +1926,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
c.emitStoreByIndex(varGlobal, c.exceptionIndex) c.emitStoreByIndex(varGlobal, c.exceptionIndex)
case "delete": case "delete":
emit.Opcodes(c.prog.BinWriter, opcode.REMOVE) emit.Opcodes(c.prog.BinWriter, opcode.REMOVE)
case "FromAddress", "ToHash160": case "ToHash160":
// We can be sure that this is an ast.BasicLit just containing a simple // We can be sure that this is an ast.BasicLit just containing a simple
// address string. Note that the string returned from calling Value will // address string. Note that the string returned from calling Value will
// contain double quotes that need to be stripped. // contain double quotes that need to be stripped.
@ -1946,7 +1946,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
// transformArgs returns a list of function arguments // transformArgs returns a list of function arguments
// which should be put on stack. // which should be put on stack.
// There are special cases for builtins: // There are special cases for builtins:
// 1. With FromAddress and with ToHash160 in case if it behaves like builtin, // 1. With ToHash160 in case if it behaves like builtin,
// parameter conversion is happening at compile-time so there is no need to // parameter conversion is happening at compile-time so there is no need to
// push parameters on stack and perform an actual call // push parameters on stack and perform an actual call
// 2. With panic, the generated code depends on the fact if an argument was nil or a string; // 2. With panic, the generated code depends on the fact if an argument was nil or a string;
@ -1954,7 +1954,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
func transformArgs(fs *funcScope, fun ast.Expr, isBuiltin bool, args []ast.Expr) []ast.Expr { func transformArgs(fs *funcScope, fun ast.Expr, isBuiltin bool, args []ast.Expr) []ast.Expr {
switch f := fun.(type) { switch f := fun.(type) {
case *ast.SelectorExpr: case *ast.SelectorExpr:
if f.Sel.Name == "FromAddress" || (isBuiltin && f.Sel.Name == "ToHash160") { if isBuiltin && f.Sel.Name == "ToHash160" {
return args[1:] return args[1:]
} }
if fs != nil && isSyscall(fs) { if fs != nil && isSyscall(fs) {

View file

@ -89,10 +89,10 @@ func TestNotAssignedFunctionCall(t *testing.T) {
}) })
t.Run("Builtin", func(t *testing.T) { t.Run("Builtin", func(t *testing.T) {
src := `package foo src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/util" import "github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
func Main() int { func Main() int {
util.FromAddress("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8") address.ToHash160("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8")
util.FromAddress("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8") address.ToHash160("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8")
return 1 return 1
}` }`
eval(t, src, big.NewInt(1)) eval(t, src, big.NewInt(1))

View file

@ -71,62 +71,13 @@ func TestTypeConstantSize(t *testing.T) {
}) })
} }
func TestFromAddress(t *testing.T) {
as1 := "NQRLhCpAru9BjGsMwk67vdMwmzKMRgsnnN"
addr1, err := address.StringToUint160(as1)
require.NoError(t, err)
as2 := "NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8"
addr2, err := address.StringToUint160(as2)
require.NoError(t, err)
t.Run("append 2 addresses", func(t *testing.T) {
src := `
package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/util"
func Main() []byte {
addr1 := util.FromAddress("` + as1 + `")
addr2 := util.FromAddress("` + as2 + `")
sum := append(addr1, addr2...)
return sum
}
`
eval(t, src, append(addr1.BytesBE(), addr2.BytesBE()...))
})
t.Run("append 2 addresses inline", func(t *testing.T) {
src := `
package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/util"
func Main() []byte {
addr1 := util.FromAddress("` + as1 + `")
sum := append(addr1, util.FromAddress("` + as2 + `")...)
return sum
}
`
eval(t, src, append(addr1.BytesBE(), addr2.BytesBE()...))
})
t.Run("AliasPackage", func(t *testing.T) {
src := `
package foo
import uu "github.com/nspcc-dev/neo-go/pkg/interop/util"
func Main() []byte {
addr1 := uu.FromAddress("` + as1 + `")
addr2 := uu.FromAddress("` + as2 + `")
sum := append(addr1, addr2...)
return sum
}`
eval(t, src, append(addr1.BytesBE(), addr2.BytesBE()...))
})
}
func TestAddressToHash160BuiltinConversion(t *testing.T) { func TestAddressToHash160BuiltinConversion(t *testing.T) {
a := "NQRLhCpAru9BjGsMwk67vdMwmzKMRgsnnN" a := "NQRLhCpAru9BjGsMwk67vdMwmzKMRgsnnN"
h, err := address.StringToUint160(a) h, err := address.StringToUint160(a)
require.NoError(t, err) require.NoError(t, err)
a2 := "NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8"
addr2, err := address.StringToUint160(a2)
require.NoError(t, err)
t.Run("builtin conversion", func(t *testing.T) { t.Run("builtin conversion", func(t *testing.T) {
src := `package foo src := `package foo
import ( import (
@ -163,6 +114,18 @@ func TestAddressToHash160BuiltinConversion(t *testing.T) {
// On the contrary, there should be an address string. // On the contrary, there should be an address string.
require.True(t, strings.Contains(string(prog), a)) require.True(t, strings.Contains(string(prog), a))
}) })
t.Run("AliasPackage", func(t *testing.T) {
src := `
package foo
import ad "github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
func Main() []byte {
addr1 := ad.ToHash160("` + a + `")
addr2 := ad.ToHash160("` + a2 + `")
sum := append(addr1, addr2...)
return sum
}`
eval(t, src, append(h.BytesBE(), addr2.BytesBE()...))
})
} }
func TestInvokeAddressToFromHash160(t *testing.T) { func TestInvokeAddressToFromHash160(t *testing.T) {

View file

@ -650,10 +650,10 @@ func TestBlockchain_IsTxStillRelevant(t *testing.T) {
src := fmt.Sprintf(`package verify src := fmt.Sprintf(`package verify
import ( import (
"github.com/nspcc-dev/neo-go/pkg/interop/contract" "github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/util" "github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
) )
func Verify() bool { func Verify() bool {
addr := util.FromAddress("`+address.Uint160ToString(e.NativeHash(t, nativenames.Ledger))+`") addr := address.ToHash160("`+address.Uint160ToString(e.NativeHash(t, nativenames.Ledger))+`")
currentHeight := contract.Call(addr, "currentIndex", contract.ReadStates) currentHeight := contract.Call(addr, "currentIndex", contract.ReadStates)
return currentHeight.(int) < %d return currentHeight.(int) < %d
}`, bc.BlockHeight()+2) // deploy + next block }`, bc.BlockHeight()+2) // deploy + next block

View file

@ -14,16 +14,6 @@ func Abort() {
neogointernal.Opcode0NoReturn("ABORT") neogointernal.Opcode0NoReturn("ABORT")
} }
// FromAddress is an utility function that converts a Neo address to its hash
// (160 bit BE value in a 20 byte slice). It can only be used for strings known
// at compilation time, because the conversion is actually being done by the
// compiler.
//
// Deprecated: use address.ToHash160 instead.
func FromAddress(address string) interop.Hash160 {
return nil
}
// Equals compares a with b and will return true when a and b are equal. It's // Equals compares a with b and will return true when a and b are equal. It's
// implemented as an EQUAL VM opcode, so the rules of comparison are those // implemented as an EQUAL VM opcode, so the rules of comparison are those
// of EQUAL. // of EQUAL.

View file

@ -1,7 +1,6 @@
package rpcsrv package rpcsrv
import ( import (
"fmt"
"strings" "strings"
"time" "time"
@ -10,8 +9,7 @@ import (
// Metrics used in monitoring service. // Metrics used in monitoring service.
var ( var (
rpcCounter = map[string]prometheus.Counter{} rpcTimes = map[string]prometheus.Histogram{}
rpcTimes = map[string]prometheus.Histogram{}
) )
func addReqTimeMetric(name string, t time.Duration) { func addReqTimeMetric(name string, t time.Duration) {
@ -19,22 +17,9 @@ func addReqTimeMetric(name string, t time.Duration) {
if ok { if ok {
hist.Observe(t.Seconds()) hist.Observe(t.Seconds())
} }
ctr, ok := rpcCounter[name]
if ok {
ctr.Inc()
}
} }
func regCounter(call string) { func regCounter(call string) {
ctr := prometheus.NewCounter(
prometheus.CounterOpts{
Help: fmt.Sprintf("Number of calls to %s rpc endpoint (obsolete, to be removed)", call),
Name: fmt.Sprintf("%s_called", call),
Namespace: "neogo",
},
)
prometheus.MustRegister(ctr)
rpcCounter[call] = ctr
rpcTimes[call] = prometheus.NewHistogram( rpcTimes[call] = prometheus.NewHistogram(
prometheus.HistogramOpts{ prometheus.HistogramOpts{
Help: "RPC " + call + " call handling time", Help: "RPC " + call + " call handling time",