wallet: allow complex contract verification schemes, close #3015
This was recently added in neotest, but working with the real RPC is still not enjoyable. This commit extends `wallet.Account` with invocation script builder to aid network fee calculations and signing. Signed-off-by: Evgenii Stratonikov <fyfyrchik@runbox.com>
This commit is contained in:
parent
de98b39a95
commit
962b161652
2 changed files with 44 additions and 0 deletions
|
@ -191,6 +191,14 @@ func (a *Actor) MakeUnsignedUncheckedRun(script []byte, sysFee int64, attrs []tr
|
||||||
for i := range a.signers {
|
for i := range a.signers {
|
||||||
if !a.signers[i].Account.Contract.Deployed {
|
if !a.signers[i].Account.Contract.Deployed {
|
||||||
tx.Scripts[i].VerificationScript = a.signers[i].Account.Contract.Script
|
tx.Scripts[i].VerificationScript = a.signers[i].Account.Contract.Script
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if build := a.signers[i].Account.Contract.InvocationBuilder; build != nil {
|
||||||
|
invoc, err := build(tx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("building witness for contract signer: %w", err)
|
||||||
|
}
|
||||||
|
tx.Scripts[i].InvocationScript = invoc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// CalculateNetworkFee doesn't call Hash or Size, only serializes the
|
// CalculateNetworkFee doesn't call Hash or Size, only serializes the
|
||||||
|
|
|
@ -9,8 +9,10 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,6 +57,12 @@ type Contract struct {
|
||||||
|
|
||||||
// Indicates whether the contract has been deployed to the blockchain.
|
// Indicates whether the contract has been deployed to the blockchain.
|
||||||
Deployed bool `json:"deployed"`
|
Deployed bool `json:"deployed"`
|
||||||
|
|
||||||
|
// InvocationBuilder returns invocation script for deployed contracts.
|
||||||
|
// In case contract is not deployed or has 0 arguments, this field is ignored.
|
||||||
|
// It might be executed on a partially formed tx, and is primarily needed to properly
|
||||||
|
// calculate network fee for complex contract signers.
|
||||||
|
InvocationBuilder func(tx *transaction.Transaction) ([]byte, error) `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractParam is a descriptor of a contract parameter
|
// ContractParam is a descriptor of a contract parameter
|
||||||
|
@ -78,6 +86,29 @@ func NewAccount() (*Account, error) {
|
||||||
return NewAccountFromPrivateKey(priv), nil
|
return NewAccountFromPrivateKey(priv), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewContractAccount creates a contract account belonging to some deployed contract.
|
||||||
|
// SignTx can be called on this account with no error and will create invocation script,
|
||||||
|
// which puts provided arguments on stack for use in `verify`.
|
||||||
|
func NewContractAccount(hash util.Uint160, args ...any) *Account {
|
||||||
|
return &Account{
|
||||||
|
Address: address.Uint160ToString(hash),
|
||||||
|
Contract: &Contract{
|
||||||
|
Parameters: make([]ContractParam, len(args)),
|
||||||
|
Deployed: true,
|
||||||
|
InvocationBuilder: func(tx *transaction.Transaction) ([]byte, error) {
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
for i := range args {
|
||||||
|
emit.Any(w.BinWriter, args[i])
|
||||||
|
}
|
||||||
|
if w.Err != nil {
|
||||||
|
return nil, w.Err
|
||||||
|
}
|
||||||
|
return w.Bytes(), nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SignTx signs transaction t and updates it's Witnesses.
|
// SignTx signs transaction t and updates it's Witnesses.
|
||||||
func (a *Account) SignTx(net netmode.Magic, t *transaction.Transaction) error {
|
func (a *Account) SignTx(net netmode.Magic, t *transaction.Transaction) error {
|
||||||
var (
|
var (
|
||||||
|
@ -108,6 +139,11 @@ func (a *Account) SignTx(net netmode.Magic, t *transaction.Transaction) error {
|
||||||
VerificationScript: a.Contract.Script, // Can be nil for deployed contract.
|
VerificationScript: a.Contract.Script, // Can be nil for deployed contract.
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if a.Contract.Deployed && a.Contract.InvocationBuilder != nil {
|
||||||
|
invoc, err := a.Contract.InvocationBuilder(t)
|
||||||
|
t.Scripts[pos].InvocationScript = invoc
|
||||||
|
return err
|
||||||
|
}
|
||||||
if len(a.Contract.Parameters) == 0 {
|
if len(a.Contract.Parameters) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue