transaction: add new Rules witness scope
See neo-project/neo#2622. The implementation is somewhat asymmetric (and not very efficient) for binary/JSON encoding/decoding, but it should be sufficient.
This commit is contained in:
parent
fc487eaa44
commit
9875799893
13 changed files with 1299 additions and 27 deletions
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"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/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
@ -28,6 +29,38 @@ func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) {
|
||||||
return false, errors.New("script container is not a transaction")
|
return false, errors.New("script container is not a transaction")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type scopeContext struct {
|
||||||
|
*vm.VM
|
||||||
|
ic *interop.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func getContractGroups(v *vm.VM, ic *interop.Context, h util.Uint160) (manifest.Groups, error) {
|
||||||
|
if !v.Context().GetCallFlags().Has(callflag.ReadStates) {
|
||||||
|
return nil, errors.New("missing ReadStates call flag")
|
||||||
|
}
|
||||||
|
cs, err := ic.GetContract(h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil // It's OK to not have the contract.
|
||||||
|
}
|
||||||
|
return manifest.Groups(cs.Manifest.Groups), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc scopeContext) checkScriptGroups(h util.Uint160, k *keys.PublicKey) (bool, error) {
|
||||||
|
groups, err := getContractGroups(sc.VM, sc.ic, h)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return groups.Contains(k), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc scopeContext) CallingScriptHasGroup(k *keys.PublicKey) (bool, error) {
|
||||||
|
return sc.checkScriptGroups(sc.GetCallingScriptHash(), k)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc scopeContext) CurrentScriptHasGroup(k *keys.PublicKey) (bool, error) {
|
||||||
|
return sc.checkScriptGroups(sc.GetCurrentScriptHash(), k)
|
||||||
|
}
|
||||||
|
|
||||||
func checkScope(ic *interop.Context, tx *transaction.Transaction, v *vm.VM, hash util.Uint160) (bool, error) {
|
func checkScope(ic *interop.Context, tx *transaction.Transaction, v *vm.VM, hash util.Uint160) (bool, error) {
|
||||||
for _, c := range tx.Signers {
|
for _, c := range tx.Signers {
|
||||||
if c.Account == hash {
|
if c.Account == hash {
|
||||||
|
@ -50,19 +83,26 @@ func checkScope(ic *interop.Context, tx *transaction.Transaction, v *vm.VM, hash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.Scopes&transaction.CustomGroups != 0 {
|
if c.Scopes&transaction.CustomGroups != 0 {
|
||||||
if !v.Context().GetCallFlags().Has(callflag.ReadStates) {
|
groups, err := getContractGroups(v, ic, v.GetCurrentScriptHash())
|
||||||
return false, errors.New("missing ReadStates call flag")
|
|
||||||
}
|
|
||||||
cs, err := ic.GetContract(v.GetCurrentScriptHash())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil
|
return false, err
|
||||||
}
|
}
|
||||||
// check if the current group is the required one
|
// check if the current group is the required one
|
||||||
for _, allowedGroup := range c.AllowedGroups {
|
for _, allowedGroup := range c.AllowedGroups {
|
||||||
for _, group := range cs.Manifest.Groups {
|
if groups.Contains(allowedGroup) {
|
||||||
if group.PublicKey.Equal(allowedGroup) {
|
return true, nil
|
||||||
return true, nil
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if c.Scopes&transaction.Rules != 0 {
|
||||||
|
ctx := scopeContext{v, ic}
|
||||||
|
for _, r := range c.Rules {
|
||||||
|
res, err := r.Condition.Match(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if res {
|
||||||
|
return r.Action == transaction.WitnessAllow, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1189,6 +1189,28 @@ func TestRuntimeCheckWitness(t *testing.T) {
|
||||||
ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), callflag.AllowCall)
|
ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), callflag.AllowCall)
|
||||||
check(t, ic, hash.BytesBE(), true)
|
check(t, ic, hash.BytesBE(), true)
|
||||||
})
|
})
|
||||||
|
t.Run("Rules, missing ReadStates flag", func(t *testing.T) {
|
||||||
|
hash := random.Uint160()
|
||||||
|
pk, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
tx := &transaction.Transaction{
|
||||||
|
Signers: []transaction.Signer{
|
||||||
|
{
|
||||||
|
Account: hash,
|
||||||
|
Scopes: transaction.Rules,
|
||||||
|
Rules: []transaction.WitnessRule{{
|
||||||
|
Action: transaction.WitnessAllow,
|
||||||
|
Condition: (*transaction.ConditionGroup)(pk.PublicKey()),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ic.Container = tx
|
||||||
|
callingScriptHash := scriptHash
|
||||||
|
loadScriptWithHashAndFlags(ic, script, callingScriptHash, callflag.All)
|
||||||
|
ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), callflag.AllowCall)
|
||||||
|
check(t, ic, hash.BytesBE(), true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("positive", func(t *testing.T) {
|
t.Run("positive", func(t *testing.T) {
|
||||||
|
@ -1301,6 +1323,64 @@ func TestRuntimeCheckWitness(t *testing.T) {
|
||||||
check(t, ic, targetHash.BytesBE(), false, true)
|
check(t, ic, targetHash.BytesBE(), false, true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
t.Run("Rules", func(t *testing.T) {
|
||||||
|
t.Run("no match", func(t *testing.T) {
|
||||||
|
hash := random.Uint160()
|
||||||
|
tx := &transaction.Transaction{
|
||||||
|
Signers: []transaction.Signer{
|
||||||
|
{
|
||||||
|
Account: hash,
|
||||||
|
Scopes: transaction.Rules,
|
||||||
|
Rules: []transaction.WitnessRule{{
|
||||||
|
Action: transaction.WitnessAllow,
|
||||||
|
Condition: (*transaction.ConditionScriptHash)(&hash),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates)
|
||||||
|
ic.Container = tx
|
||||||
|
check(t, ic, hash.BytesBE(), false, false)
|
||||||
|
})
|
||||||
|
t.Run("allow", func(t *testing.T) {
|
||||||
|
hash := random.Uint160()
|
||||||
|
var cond = true
|
||||||
|
tx := &transaction.Transaction{
|
||||||
|
Signers: []transaction.Signer{
|
||||||
|
{
|
||||||
|
Account: hash,
|
||||||
|
Scopes: transaction.Rules,
|
||||||
|
Rules: []transaction.WitnessRule{{
|
||||||
|
Action: transaction.WitnessAllow,
|
||||||
|
Condition: (*transaction.ConditionBoolean)(&cond),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates)
|
||||||
|
ic.Container = tx
|
||||||
|
check(t, ic, hash.BytesBE(), false, true)
|
||||||
|
})
|
||||||
|
t.Run("deny", func(t *testing.T) {
|
||||||
|
hash := random.Uint160()
|
||||||
|
var cond = true
|
||||||
|
tx := &transaction.Transaction{
|
||||||
|
Signers: []transaction.Signer{
|
||||||
|
{
|
||||||
|
Account: hash,
|
||||||
|
Scopes: transaction.Rules,
|
||||||
|
Rules: []transaction.WitnessRule{{
|
||||||
|
Action: transaction.WitnessDeny,
|
||||||
|
Condition: (*transaction.ConditionBoolean)(&cond),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates)
|
||||||
|
ic.Container = tx
|
||||||
|
check(t, ic, hash.BytesBE(), false, false)
|
||||||
|
})
|
||||||
|
})
|
||||||
t.Run("bad scope", func(t *testing.T) {
|
t.Run("bad scope", func(t *testing.T) {
|
||||||
hash := random.Uint160()
|
hash := random.Uint160()
|
||||||
tx := &transaction.Transaction{
|
tx := &transaction.Transaction{
|
||||||
|
|
|
@ -17,6 +17,7 @@ type Signer struct {
|
||||||
Scopes WitnessScope `json:"scopes"`
|
Scopes WitnessScope `json:"scopes"`
|
||||||
AllowedContracts []util.Uint160 `json:"allowedcontracts,omitempty"`
|
AllowedContracts []util.Uint160 `json:"allowedcontracts,omitempty"`
|
||||||
AllowedGroups []*keys.PublicKey `json:"allowedgroups,omitempty"`
|
AllowedGroups []*keys.PublicKey `json:"allowedgroups,omitempty"`
|
||||||
|
Rules []WitnessRule `json:"rules,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBinary implements Serializable interface.
|
// EncodeBinary implements Serializable interface.
|
||||||
|
@ -29,6 +30,9 @@ func (c *Signer) EncodeBinary(bw *io.BinWriter) {
|
||||||
if c.Scopes&CustomGroups != 0 {
|
if c.Scopes&CustomGroups != 0 {
|
||||||
bw.WriteArray(c.AllowedGroups)
|
bw.WriteArray(c.AllowedGroups)
|
||||||
}
|
}
|
||||||
|
if c.Scopes&Rules != 0 {
|
||||||
|
bw.WriteArray(c.Rules)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements Serializable interface.
|
// DecodeBinary implements Serializable interface.
|
||||||
|
@ -49,4 +53,7 @@ func (c *Signer) DecodeBinary(br *io.BinReader) {
|
||||||
if c.Scopes&CustomGroups != 0 {
|
if c.Scopes&CustomGroups != 0 {
|
||||||
br.ReadArray(&c.AllowedGroups, maxSubitems)
|
br.ReadArray(&c.AllowedGroups, maxSubitems)
|
||||||
}
|
}
|
||||||
|
if c.Scopes&Rules != 0 {
|
||||||
|
br.ReadArray(&c.Rules, maxSubitems)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
575
pkg/core/transaction/witness_condition.go
Normal file
575
pkg/core/transaction/witness_condition.go
Normal file
|
@ -0,0 +1,575 @@
|
||||||
|
package transaction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate stringer -type=WitnessConditionType -linecomment
|
||||||
|
|
||||||
|
// WitnessConditionType encodes a type of witness condition.
|
||||||
|
type WitnessConditionType byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
// WitnessBoolean is a generic boolean condition.
|
||||||
|
WitnessBoolean WitnessConditionType = 0x00 // Boolean
|
||||||
|
// WitnessNot reverses another condition.
|
||||||
|
WitnessNot WitnessConditionType = 0x01 // Not
|
||||||
|
// WitnessAnd means that all conditions must be met.
|
||||||
|
WitnessAnd WitnessConditionType = 0x02 // And
|
||||||
|
// WitnessOr means that any of conditions must be met.
|
||||||
|
WitnessOr WitnessConditionType = 0x03 // Or
|
||||||
|
// WitnessScriptHash matches executing contract's script hash.
|
||||||
|
WitnessScriptHash WitnessConditionType = 0x18 // ScriptHash
|
||||||
|
// WitnessGroup matches executing contract's group key.
|
||||||
|
WitnessGroup WitnessConditionType = 0x19 // Group
|
||||||
|
// WitnessCalledByEntry matches when current script is an entry script or is called by an entry script.
|
||||||
|
WitnessCalledByEntry WitnessConditionType = 0x20 // CalledByEntry
|
||||||
|
// WitnessCalledByContract matches when current script is called by the specified contract.
|
||||||
|
WitnessCalledByContract WitnessConditionType = 0x28 // CalledByContract
|
||||||
|
// WitnessCalledByGroup matches when current script is called by contract belonging to the specified group.
|
||||||
|
WitnessCalledByGroup WitnessConditionType = 0x29 // CalledByGroup
|
||||||
|
|
||||||
|
// MaxConditionNesting limits the maximum allowed level of condition nesting.
|
||||||
|
MaxConditionNesting = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// WitnessCondition is a condition of WitnessRule.
|
||||||
|
type WitnessCondition interface {
|
||||||
|
// Type returns a type of this condition.
|
||||||
|
Type() WitnessConditionType
|
||||||
|
// Match checks whether this condition matches current context.
|
||||||
|
Match(MatchContext) (bool, error)
|
||||||
|
// EncodeBinary allows to serialize condition to its binary
|
||||||
|
// representation (including type data).
|
||||||
|
EncodeBinary(*io.BinWriter)
|
||||||
|
// DecodeBinarySpecific decodes type-specific binary data from the given
|
||||||
|
// reader (not including type data).
|
||||||
|
DecodeBinarySpecific(*io.BinReader, int)
|
||||||
|
|
||||||
|
json.Marshaler
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchContext is a set of methods from execution engine needed to perform the
|
||||||
|
// witness check.
|
||||||
|
type MatchContext interface {
|
||||||
|
GetCallingScriptHash() util.Uint160
|
||||||
|
GetCurrentScriptHash() util.Uint160
|
||||||
|
GetEntryScriptHash() util.Uint160
|
||||||
|
CallingScriptHasGroup(*keys.PublicKey) (bool, error)
|
||||||
|
CurrentScriptHasGroup(*keys.PublicKey) (bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
// ConditionBoolean is a boolean condition type.
|
||||||
|
ConditionBoolean bool
|
||||||
|
// ConditionNot inverses the meaning of contained condition.
|
||||||
|
ConditionNot struct {
|
||||||
|
Condition WitnessCondition
|
||||||
|
}
|
||||||
|
// ConditionAnd is a set of conditions required to match.
|
||||||
|
ConditionAnd []WitnessCondition
|
||||||
|
// ConditionOr is a set of conditions one of which is required to match.
|
||||||
|
ConditionOr []WitnessCondition
|
||||||
|
// ConditionScriptHash is a condition matching executing script hash.
|
||||||
|
ConditionScriptHash util.Uint160
|
||||||
|
// ConditionGroup is a condition matching executing script group.
|
||||||
|
ConditionGroup keys.PublicKey
|
||||||
|
// ConditionCalledByEntry is a condition matching entry script or one directly called by it.
|
||||||
|
ConditionCalledByEntry struct{}
|
||||||
|
// ConditionCalledByContract is a condition matching calling script hash.
|
||||||
|
ConditionCalledByContract util.Uint160
|
||||||
|
// ConditionCalledByGroup is a condition matching calling script group.
|
||||||
|
ConditionCalledByGroup keys.PublicKey
|
||||||
|
)
|
||||||
|
|
||||||
|
// conditionAux is used for JSON marshaling/unmarshaling.
|
||||||
|
type conditionAux struct {
|
||||||
|
Expression json.RawMessage `json:"expression,omitempty"` // Can be either boolean or conditionAux.
|
||||||
|
Expressions []json.RawMessage `json:"expressions,omitempty"`
|
||||||
|
Group *keys.PublicKey `json:"group,omitempty"`
|
||||||
|
Hash *util.Uint160 `json:"hash,omitempty"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements WitnessCondition interface and returns condition type.
|
||||||
|
func (c *ConditionBoolean) Type() WitnessConditionType {
|
||||||
|
return WitnessBoolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements WitnessCondition interface checking whether this condition
|
||||||
|
// matches given context.
|
||||||
|
func (c *ConditionBoolean) Match(_ MatchContext) (bool, error) {
|
||||||
|
return bool(*c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements WitnessCondition interface allowing to serialize condition.
|
||||||
|
func (c *ConditionBoolean) EncodeBinary(w *io.BinWriter) {
|
||||||
|
w.WriteB(byte(c.Type()))
|
||||||
|
w.WriteBool(bool(*c))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinarySpecific implements WitnessCondition interface allowing to
|
||||||
|
// deserialize condition-specific data.
|
||||||
|
func (c *ConditionBoolean) DecodeBinarySpecific(r *io.BinReader, maxDepth int) {
|
||||||
|
*c = ConditionBoolean(r.ReadBool())
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (c *ConditionBoolean) MarshalJSON() ([]byte, error) {
|
||||||
|
boolJSON, _ := json.Marshal(bool(*c)) // Simple boolean can't fail.
|
||||||
|
aux := conditionAux{
|
||||||
|
Type: c.Type().String(),
|
||||||
|
Expression: json.RawMessage(boolJSON),
|
||||||
|
}
|
||||||
|
return json.Marshal(aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements WitnessCondition interface and returns condition type.
|
||||||
|
func (c *ConditionNot) Type() WitnessConditionType {
|
||||||
|
return WitnessNot
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements WitnessCondition interface checking whether this condition
|
||||||
|
// matches given context.
|
||||||
|
func (c *ConditionNot) Match(ctx MatchContext) (bool, error) {
|
||||||
|
res, err := c.Condition.Match(ctx)
|
||||||
|
return ((err == nil) && !res), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements WitnessCondition interface allowing to serialize condition.
|
||||||
|
func (c *ConditionNot) EncodeBinary(w *io.BinWriter) {
|
||||||
|
w.WriteB(byte(c.Type()))
|
||||||
|
c.Condition.EncodeBinary(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinarySpecific implements WitnessCondition interface allowing to
|
||||||
|
// deserialize condition-specific data.
|
||||||
|
func (c *ConditionNot) DecodeBinarySpecific(r *io.BinReader, maxDepth int) {
|
||||||
|
c.Condition = decodeBinaryCondition(r, maxDepth-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (c *ConditionNot) MarshalJSON() ([]byte, error) {
|
||||||
|
condJSON, err := json.Marshal(c.Condition)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aux := conditionAux{
|
||||||
|
Type: c.Type().String(),
|
||||||
|
Expression: json.RawMessage(condJSON),
|
||||||
|
}
|
||||||
|
return json.Marshal(aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements WitnessCondition interface and returns condition type.
|
||||||
|
func (c *ConditionAnd) Type() WitnessConditionType {
|
||||||
|
return WitnessAnd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements WitnessCondition interface checking whether this condition
|
||||||
|
// matches given context.
|
||||||
|
func (c *ConditionAnd) Match(ctx MatchContext) (bool, error) {
|
||||||
|
for _, cond := range *c {
|
||||||
|
res, err := cond.Match(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if !res {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements WitnessCondition interface allowing to serialize condition.
|
||||||
|
func (c *ConditionAnd) EncodeBinary(w *io.BinWriter) {
|
||||||
|
w.WriteB(byte(c.Type()))
|
||||||
|
w.WriteArray([]WitnessCondition(*c))
|
||||||
|
}
|
||||||
|
|
||||||
|
func readArrayOfConditions(r *io.BinReader, maxDepth int) []WitnessCondition {
|
||||||
|
l := r.ReadVarUint()
|
||||||
|
if l == 0 {
|
||||||
|
r.Err = errors.New("empty array of conditions")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if l > maxSubitems {
|
||||||
|
r.Err = errors.New("too many elements")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
a := make([]WitnessCondition, l)
|
||||||
|
for i := 0; i < int(l); i++ {
|
||||||
|
a[i] = decodeBinaryCondition(r, maxDepth-1)
|
||||||
|
}
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinarySpecific implements WitnessCondition interface allowing to
|
||||||
|
// deserialize condition-specific data.
|
||||||
|
func (c *ConditionAnd) DecodeBinarySpecific(r *io.BinReader, maxDepth int) {
|
||||||
|
a := readArrayOfConditions(r, maxDepth)
|
||||||
|
if r.Err == nil {
|
||||||
|
*c = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func arrayToJSON(c WitnessCondition, a []WitnessCondition) ([]byte, error) {
|
||||||
|
exprs := make([]json.RawMessage, len(a))
|
||||||
|
for i := 0; i < len(a); i++ {
|
||||||
|
b, err := a[i].MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
exprs[i] = json.RawMessage(b)
|
||||||
|
}
|
||||||
|
aux := conditionAux{
|
||||||
|
Type: c.Type().String(),
|
||||||
|
Expressions: exprs,
|
||||||
|
}
|
||||||
|
return json.Marshal(aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (c *ConditionAnd) MarshalJSON() ([]byte, error) {
|
||||||
|
return arrayToJSON(c, []WitnessCondition(*c))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements WitnessCondition interface and returns condition type.
|
||||||
|
func (c *ConditionOr) Type() WitnessConditionType {
|
||||||
|
return WitnessOr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements WitnessCondition interface checking whether this condition
|
||||||
|
// matches given context.
|
||||||
|
func (c *ConditionOr) Match(ctx MatchContext) (bool, error) {
|
||||||
|
for _, cond := range *c {
|
||||||
|
res, err := cond.Match(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if res {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements WitnessCondition interface allowing to serialize condition.
|
||||||
|
func (c *ConditionOr) EncodeBinary(w *io.BinWriter) {
|
||||||
|
w.WriteB(byte(c.Type()))
|
||||||
|
w.WriteArray([]WitnessCondition(*c))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinarySpecific implements WitnessCondition interface allowing to
|
||||||
|
// deserialize condition-specific data.
|
||||||
|
func (c *ConditionOr) DecodeBinarySpecific(r *io.BinReader, maxDepth int) {
|
||||||
|
a := readArrayOfConditions(r, maxDepth)
|
||||||
|
if r.Err == nil {
|
||||||
|
*c = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (c *ConditionOr) MarshalJSON() ([]byte, error) {
|
||||||
|
return arrayToJSON(c, []WitnessCondition(*c))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements WitnessCondition interface and returns condition type.
|
||||||
|
func (c *ConditionScriptHash) Type() WitnessConditionType {
|
||||||
|
return WitnessScriptHash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements WitnessCondition interface checking whether this condition
|
||||||
|
// matches given context.
|
||||||
|
func (c *ConditionScriptHash) Match(ctx MatchContext) (bool, error) {
|
||||||
|
return util.Uint160(*c).Equals(ctx.GetCurrentScriptHash()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements WitnessCondition interface allowing to serialize condition.
|
||||||
|
func (c *ConditionScriptHash) EncodeBinary(w *io.BinWriter) {
|
||||||
|
w.WriteB(byte(c.Type()))
|
||||||
|
w.WriteBytes(c[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinarySpecific implements WitnessCondition interface allowing to
|
||||||
|
// deserialize condition-specific data.
|
||||||
|
func (c *ConditionScriptHash) DecodeBinarySpecific(r *io.BinReader, _ int) {
|
||||||
|
r.ReadBytes(c[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (c *ConditionScriptHash) MarshalJSON() ([]byte, error) {
|
||||||
|
aux := conditionAux{
|
||||||
|
Type: c.Type().String(),
|
||||||
|
Hash: (*util.Uint160)(c),
|
||||||
|
}
|
||||||
|
return json.Marshal(aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements WitnessCondition interface and returns condition type.
|
||||||
|
func (c *ConditionGroup) Type() WitnessConditionType {
|
||||||
|
return WitnessGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements WitnessCondition interface checking whether this condition
|
||||||
|
// matches given context.
|
||||||
|
func (c *ConditionGroup) Match(ctx MatchContext) (bool, error) {
|
||||||
|
return ctx.CurrentScriptHasGroup((*keys.PublicKey)(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements WitnessCondition interface allowing to serialize condition.
|
||||||
|
func (c *ConditionGroup) EncodeBinary(w *io.BinWriter) {
|
||||||
|
w.WriteB(byte(c.Type()))
|
||||||
|
(*keys.PublicKey)(c).EncodeBinary(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinarySpecific implements WitnessCondition interface allowing to
|
||||||
|
// deserialize condition-specific data.
|
||||||
|
func (c *ConditionGroup) DecodeBinarySpecific(r *io.BinReader, _ int) {
|
||||||
|
(*keys.PublicKey)(c).DecodeBinary(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (c *ConditionGroup) MarshalJSON() ([]byte, error) {
|
||||||
|
aux := conditionAux{
|
||||||
|
Type: c.Type().String(),
|
||||||
|
Group: (*keys.PublicKey)(c),
|
||||||
|
}
|
||||||
|
return json.Marshal(aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements WitnessCondition interface and returns condition type.
|
||||||
|
func (c ConditionCalledByEntry) Type() WitnessConditionType {
|
||||||
|
return WitnessCalledByEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements WitnessCondition interface checking whether this condition
|
||||||
|
// matches given context.
|
||||||
|
func (c ConditionCalledByEntry) Match(ctx MatchContext) (bool, error) {
|
||||||
|
entry := ctx.GetEntryScriptHash()
|
||||||
|
return entry.Equals(ctx.GetCallingScriptHash()) || entry.Equals(ctx.GetCurrentScriptHash()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements WitnessCondition interface allowing to serialize condition.
|
||||||
|
func (c ConditionCalledByEntry) EncodeBinary(w *io.BinWriter) {
|
||||||
|
w.WriteB(byte(c.Type()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinarySpecific implements WitnessCondition interface allowing to
|
||||||
|
// deserialize condition-specific data.
|
||||||
|
func (c ConditionCalledByEntry) DecodeBinarySpecific(_ *io.BinReader, _ int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (c ConditionCalledByEntry) MarshalJSON() ([]byte, error) {
|
||||||
|
aux := conditionAux{
|
||||||
|
Type: c.Type().String(),
|
||||||
|
}
|
||||||
|
return json.Marshal(aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements WitnessCondition interface and returns condition type.
|
||||||
|
func (c *ConditionCalledByContract) Type() WitnessConditionType {
|
||||||
|
return WitnessCalledByContract
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements WitnessCondition interface checking whether this condition
|
||||||
|
// matches given context.
|
||||||
|
func (c *ConditionCalledByContract) Match(ctx MatchContext) (bool, error) {
|
||||||
|
return util.Uint160(*c).Equals(ctx.GetCallingScriptHash()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements WitnessCondition interface allowing to serialize condition.
|
||||||
|
func (c *ConditionCalledByContract) EncodeBinary(w *io.BinWriter) {
|
||||||
|
w.WriteB(byte(c.Type()))
|
||||||
|
w.WriteBytes(c[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinarySpecific implements WitnessCondition interface allowing to
|
||||||
|
// deserialize condition-specific data.
|
||||||
|
func (c *ConditionCalledByContract) DecodeBinarySpecific(r *io.BinReader, _ int) {
|
||||||
|
r.ReadBytes(c[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (c *ConditionCalledByContract) MarshalJSON() ([]byte, error) {
|
||||||
|
aux := conditionAux{
|
||||||
|
Type: c.Type().String(),
|
||||||
|
Hash: (*util.Uint160)(c),
|
||||||
|
}
|
||||||
|
return json.Marshal(aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements WitnessCondition interface and returns condition type.
|
||||||
|
func (c *ConditionCalledByGroup) Type() WitnessConditionType {
|
||||||
|
return WitnessCalledByGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match implements WitnessCondition interface checking whether this condition
|
||||||
|
// matches given context.
|
||||||
|
func (c *ConditionCalledByGroup) Match(ctx MatchContext) (bool, error) {
|
||||||
|
return ctx.CallingScriptHasGroup((*keys.PublicKey)(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements WitnessCondition interface allowing to serialize condition.
|
||||||
|
func (c *ConditionCalledByGroup) EncodeBinary(w *io.BinWriter) {
|
||||||
|
w.WriteB(byte(c.Type()))
|
||||||
|
(*keys.PublicKey)(c).EncodeBinary(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinarySpecific implements WitnessCondition interface allowing to
|
||||||
|
// deserialize condition-specific data.
|
||||||
|
func (c *ConditionCalledByGroup) DecodeBinarySpecific(r *io.BinReader, _ int) {
|
||||||
|
(*keys.PublicKey)(c).DecodeBinary(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
func (c *ConditionCalledByGroup) MarshalJSON() ([]byte, error) {
|
||||||
|
aux := conditionAux{
|
||||||
|
Type: c.Type().String(),
|
||||||
|
Group: (*keys.PublicKey)(c),
|
||||||
|
}
|
||||||
|
return json.Marshal(aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinaryCondition decodes and returns condition from the given binary stream.
|
||||||
|
func DecodeBinaryCondition(r *io.BinReader) WitnessCondition {
|
||||||
|
return decodeBinaryCondition(r, MaxConditionNesting)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeBinaryCondition(r *io.BinReader, maxDepth int) WitnessCondition {
|
||||||
|
if maxDepth <= 0 {
|
||||||
|
r.Err = errors.New("too many nesting levels")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t := WitnessConditionType(r.ReadB())
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var res WitnessCondition
|
||||||
|
switch t {
|
||||||
|
case WitnessBoolean:
|
||||||
|
var v ConditionBoolean
|
||||||
|
res = &v
|
||||||
|
case WitnessNot:
|
||||||
|
res = &ConditionNot{}
|
||||||
|
case WitnessAnd:
|
||||||
|
res = &ConditionAnd{}
|
||||||
|
case WitnessOr:
|
||||||
|
res = &ConditionOr{}
|
||||||
|
case WitnessScriptHash:
|
||||||
|
res = &ConditionScriptHash{}
|
||||||
|
case WitnessGroup:
|
||||||
|
res = &ConditionGroup{}
|
||||||
|
case WitnessCalledByEntry:
|
||||||
|
res = ConditionCalledByEntry{}
|
||||||
|
case WitnessCalledByContract:
|
||||||
|
res = &ConditionCalledByContract{}
|
||||||
|
case WitnessCalledByGroup:
|
||||||
|
res = &ConditionCalledByGroup{}
|
||||||
|
default:
|
||||||
|
r.Err = errors.New("invalid condition type")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
res.DecodeBinarySpecific(r, maxDepth)
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalArrayOfConditionJSONs(arr []json.RawMessage, maxDepth int) ([]WitnessCondition, error) {
|
||||||
|
l := len(arr)
|
||||||
|
if l == 0 {
|
||||||
|
return nil, errors.New("empty array of conditions")
|
||||||
|
}
|
||||||
|
if l >= maxSubitems {
|
||||||
|
return nil, errors.New("too many elements")
|
||||||
|
}
|
||||||
|
res := make([]WitnessCondition, l)
|
||||||
|
for i := range arr {
|
||||||
|
v, err := unmarshalConditionJSON(arr[i], maxDepth-1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res[i] = v
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalConditionJSON unmarshalls condition from the given JSON data.
|
||||||
|
func UnmarshalConditionJSON(data []byte) (WitnessCondition, error) {
|
||||||
|
return unmarshalConditionJSON(data, MaxConditionNesting)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalConditionJSON(data []byte, maxDepth int) (WitnessCondition, error) {
|
||||||
|
if maxDepth <= 0 {
|
||||||
|
return nil, errors.New("too many nesting levels")
|
||||||
|
}
|
||||||
|
aux := &conditionAux{}
|
||||||
|
err := json.Unmarshal(data, aux)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var res WitnessCondition
|
||||||
|
switch aux.Type {
|
||||||
|
case WitnessBoolean.String():
|
||||||
|
var v bool
|
||||||
|
err = json.Unmarshal(aux.Expression, &v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res = (*ConditionBoolean)(&v)
|
||||||
|
case WitnessNot.String():
|
||||||
|
v, err := unmarshalConditionJSON(aux.Expression, maxDepth-1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res = &ConditionNot{Condition: v}
|
||||||
|
case WitnessAnd.String():
|
||||||
|
v, err := unmarshalArrayOfConditionJSONs(aux.Expressions, maxDepth)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res = (*ConditionAnd)(&v)
|
||||||
|
case WitnessOr.String():
|
||||||
|
v, err := unmarshalArrayOfConditionJSONs(aux.Expressions, maxDepth)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res = (*ConditionOr)(&v)
|
||||||
|
case WitnessScriptHash.String():
|
||||||
|
if aux.Hash == nil {
|
||||||
|
return nil, errors.New("no hash specified")
|
||||||
|
}
|
||||||
|
res = (*ConditionScriptHash)(aux.Hash)
|
||||||
|
case WitnessGroup.String():
|
||||||
|
if aux.Group == nil {
|
||||||
|
return nil, errors.New("no group specified")
|
||||||
|
}
|
||||||
|
res = (*ConditionGroup)(aux.Group)
|
||||||
|
case WitnessCalledByEntry.String():
|
||||||
|
res = ConditionCalledByEntry{}
|
||||||
|
case WitnessCalledByContract.String():
|
||||||
|
if aux.Hash == nil {
|
||||||
|
return nil, errors.New("no hash specified")
|
||||||
|
}
|
||||||
|
res = (*ConditionCalledByContract)(aux.Hash)
|
||||||
|
case WitnessCalledByGroup.String():
|
||||||
|
if aux.Group == nil {
|
||||||
|
return nil, errors.New("no group specified")
|
||||||
|
}
|
||||||
|
res = (*ConditionCalledByGroup)(aux.Group)
|
||||||
|
default:
|
||||||
|
return nil, errors.New("invalid condition type")
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
325
pkg/core/transaction/witness_condition_test.go
Normal file
325
pkg/core/transaction/witness_condition_test.go
Normal file
|
@ -0,0 +1,325 @@
|
||||||
|
package transaction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InvalidCondition struct{}
|
||||||
|
|
||||||
|
func (c InvalidCondition) Type() WitnessConditionType {
|
||||||
|
return 0xff
|
||||||
|
}
|
||||||
|
func (c InvalidCondition) Match(_ MatchContext) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
func (c InvalidCondition) EncodeBinary(w *io.BinWriter) {
|
||||||
|
w.WriteB(byte(c.Type()))
|
||||||
|
}
|
||||||
|
func (c InvalidCondition) DecodeBinarySpecific(r *io.BinReader, _ int) {
|
||||||
|
}
|
||||||
|
func (c InvalidCondition) MarshalJSON() ([]byte, error) {
|
||||||
|
aux := conditionAux{
|
||||||
|
Type: c.Type().String(),
|
||||||
|
}
|
||||||
|
return json.Marshal(aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
type condCase struct {
|
||||||
|
condition WitnessCondition
|
||||||
|
success bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWitnessConditionSerDes(t *testing.T) {
|
||||||
|
var someBool bool
|
||||||
|
pk, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
var cases = []condCase{
|
||||||
|
{(*ConditionBoolean)(&someBool), true},
|
||||||
|
{&ConditionNot{(*ConditionBoolean)(&someBool)}, true},
|
||||||
|
{&ConditionAnd{(*ConditionBoolean)(&someBool), (*ConditionBoolean)(&someBool)}, true},
|
||||||
|
{&ConditionOr{(*ConditionBoolean)(&someBool), (*ConditionBoolean)(&someBool)}, true},
|
||||||
|
{&ConditionScriptHash{1, 2, 3}, true},
|
||||||
|
{(*ConditionGroup)(pk.PublicKey()), true},
|
||||||
|
{ConditionCalledByEntry{}, true},
|
||||||
|
{&ConditionCalledByContract{1, 2, 3}, true},
|
||||||
|
{(*ConditionCalledByGroup)(pk.PublicKey()), true},
|
||||||
|
{InvalidCondition{}, false},
|
||||||
|
{&ConditionAnd{}, false},
|
||||||
|
{&ConditionOr{}, false},
|
||||||
|
{&ConditionNot{&ConditionNot{&ConditionNot{(*ConditionBoolean)(&someBool)}}}, false},
|
||||||
|
}
|
||||||
|
var maxSubCondAnd = &ConditionAnd{}
|
||||||
|
var maxSubCondOr = &ConditionAnd{}
|
||||||
|
for i := 0; i < maxSubitems+1; i++ {
|
||||||
|
*maxSubCondAnd = append(*maxSubCondAnd, (*ConditionBoolean)(&someBool))
|
||||||
|
*maxSubCondOr = append(*maxSubCondOr, (*ConditionBoolean)(&someBool))
|
||||||
|
}
|
||||||
|
cases = append(cases, condCase{maxSubCondAnd, false})
|
||||||
|
cases = append(cases, condCase{maxSubCondOr, false})
|
||||||
|
t.Run("binary", func(t *testing.T) {
|
||||||
|
for i, c := range cases {
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
c.condition.EncodeBinary(w.BinWriter)
|
||||||
|
require.NoError(t, w.Err)
|
||||||
|
b := w.Bytes()
|
||||||
|
|
||||||
|
r := io.NewBinReaderFromBuf(b)
|
||||||
|
res := DecodeBinaryCondition(r)
|
||||||
|
if !c.success {
|
||||||
|
require.Nil(t, res)
|
||||||
|
require.Errorf(t, r.Err, "case %d", i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
require.NoErrorf(t, r.Err, "case %d", i)
|
||||||
|
require.Equal(t, c.condition, res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("json", func(t *testing.T) {
|
||||||
|
for i, c := range cases {
|
||||||
|
jj, err := c.condition.MarshalJSON()
|
||||||
|
require.NoError(t, err)
|
||||||
|
res, err := UnmarshalConditionJSON(jj)
|
||||||
|
if !c.success {
|
||||||
|
require.Errorf(t, err, "case %d, json %s", i, jj)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
require.NoErrorf(t, err, "case %d, json %s", i, jj)
|
||||||
|
require.Equal(t, c.condition, res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWitnessConditionZeroDeser(t *testing.T) {
|
||||||
|
r := io.NewBinReaderFromBuf([]byte{})
|
||||||
|
res := DecodeBinaryCondition(r)
|
||||||
|
require.Nil(t, res)
|
||||||
|
require.Error(t, r.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWitnessConditionJSONErrors(t *testing.T) {
|
||||||
|
var cases = []string{
|
||||||
|
`[]`,
|
||||||
|
`{}`,
|
||||||
|
`{"type":"Boolean"}`,
|
||||||
|
`{"type":"Not"}`,
|
||||||
|
`{"type":"And"}`,
|
||||||
|
`{"type":"Or"}`,
|
||||||
|
`{"type":"ScriptHash"}`,
|
||||||
|
`{"type":"Group"}`,
|
||||||
|
`{"type":"CalledByContract"}`,
|
||||||
|
`{"type":"CalledByGroup"}`,
|
||||||
|
`{"type":"Boolean", "expression":42}`,
|
||||||
|
`{"type":"Not", "expression":true}`,
|
||||||
|
`{"type":"And", "expressions":[{"type":"CalledByGroup"},{"type":"Not", "expression":true}]}`,
|
||||||
|
`{"type":"Or", "expressions":{"type":"CalledByGroup"}}`,
|
||||||
|
`{"type":"Or", "expressions":[{"type":"CalledByGroup"},{"type":"Not", "expression":false}]}`,
|
||||||
|
`{"type":"ScriptHash", "hash":"1122"}`,
|
||||||
|
`{"type":"Group", "group":"032211"}`,
|
||||||
|
`{"type":"CalledByContract", "hash":"1122"}`,
|
||||||
|
`{"type":"CalledByGroup", "group":"032211"}`,
|
||||||
|
}
|
||||||
|
for i := range cases {
|
||||||
|
res, err := UnmarshalConditionJSON([]byte(cases[i]))
|
||||||
|
require.Errorf(t, err, "case %d, json %s", i, cases[i])
|
||||||
|
require.Nil(t, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestMC struct {
|
||||||
|
calling util.Uint160
|
||||||
|
current util.Uint160
|
||||||
|
entry util.Uint160
|
||||||
|
goodKey *keys.PublicKey
|
||||||
|
badKey *keys.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TestMC) GetCallingScriptHash() util.Uint160 {
|
||||||
|
return t.calling
|
||||||
|
}
|
||||||
|
func (t *TestMC) GetCurrentScriptHash() util.Uint160 {
|
||||||
|
return t.current
|
||||||
|
}
|
||||||
|
func (t *TestMC) GetEntryScriptHash() util.Uint160 {
|
||||||
|
return t.entry
|
||||||
|
}
|
||||||
|
func (t *TestMC) CallingScriptHasGroup(k *keys.PublicKey) (bool, error) {
|
||||||
|
res, err := t.CurrentScriptHasGroup(k)
|
||||||
|
return !res, err // To differentiate from current we invert the logic value.
|
||||||
|
}
|
||||||
|
func (t *TestMC) CurrentScriptHasGroup(k *keys.PublicKey) (bool, error) {
|
||||||
|
if k.Equal(t.goodKey) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if k.Equal(t.badKey) {
|
||||||
|
return false, errors.New("baaad key")
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWitnessConditionMatch(t *testing.T) {
|
||||||
|
pkGood, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
pkBad, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
pkNeutral, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
entrySC := util.Uint160{1, 2, 3}
|
||||||
|
currentSC := util.Uint160{4, 5, 6}
|
||||||
|
tmc := &TestMC{
|
||||||
|
calling: entrySC,
|
||||||
|
entry: entrySC,
|
||||||
|
current: currentSC,
|
||||||
|
goodKey: pkGood.PublicKey(),
|
||||||
|
badKey: pkBad.PublicKey(),
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("boolean", func(t *testing.T) {
|
||||||
|
var b bool
|
||||||
|
var c = (*ConditionBoolean)(&b)
|
||||||
|
res, err := c.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, res)
|
||||||
|
b = true
|
||||||
|
res, err = c.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, res)
|
||||||
|
})
|
||||||
|
t.Run("not", func(t *testing.T) {
|
||||||
|
var b bool
|
||||||
|
var cInner = (*ConditionBoolean)(&b)
|
||||||
|
var cInner2 = (*ConditionGroup)(pkBad.PublicKey())
|
||||||
|
var c = &ConditionNot{cInner}
|
||||||
|
var c2 = &ConditionNot{cInner2}
|
||||||
|
|
||||||
|
res, err := c.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, res)
|
||||||
|
b = true
|
||||||
|
res, err = c.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, res)
|
||||||
|
_, err = c2.Match(tmc)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
t.Run("and", func(t *testing.T) {
|
||||||
|
var bFalse, bTrue bool
|
||||||
|
var cInnerFalse = (*ConditionBoolean)(&bFalse)
|
||||||
|
var cInnerTrue = (*ConditionBoolean)(&bTrue)
|
||||||
|
var cInnerBad = (*ConditionGroup)(pkBad.PublicKey())
|
||||||
|
var c = &ConditionAnd{cInnerTrue, cInnerFalse, cInnerFalse}
|
||||||
|
var cBad = &ConditionAnd{cInnerTrue, cInnerBad}
|
||||||
|
|
||||||
|
bTrue = true
|
||||||
|
res, err := c.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, res)
|
||||||
|
bFalse = true
|
||||||
|
res, err = c.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, res)
|
||||||
|
|
||||||
|
_, err = cBad.Match(tmc)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
t.Run("or", func(t *testing.T) {
|
||||||
|
var bFalse, bTrue bool
|
||||||
|
var cInnerFalse = (*ConditionBoolean)(&bFalse)
|
||||||
|
var cInnerTrue = (*ConditionBoolean)(&bTrue)
|
||||||
|
var cInnerBad = (*ConditionGroup)(pkBad.PublicKey())
|
||||||
|
var c = &ConditionOr{cInnerTrue, cInnerFalse, cInnerFalse}
|
||||||
|
var cBad = &ConditionOr{cInnerTrue, cInnerBad}
|
||||||
|
|
||||||
|
bTrue = true
|
||||||
|
res, err := c.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, res)
|
||||||
|
bTrue = false
|
||||||
|
res, err = c.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, res)
|
||||||
|
|
||||||
|
_, err = cBad.Match(tmc)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
t.Run("script hash", func(t *testing.T) {
|
||||||
|
var cEntry = (*ConditionScriptHash)(&entrySC)
|
||||||
|
var cCurrent = (*ConditionScriptHash)(¤tSC)
|
||||||
|
|
||||||
|
res, err := cEntry.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, res)
|
||||||
|
res, err = cCurrent.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, res)
|
||||||
|
})
|
||||||
|
t.Run("group", func(t *testing.T) {
|
||||||
|
var cBad = (*ConditionGroup)(pkBad.PublicKey())
|
||||||
|
var cGood = (*ConditionGroup)(pkGood.PublicKey())
|
||||||
|
var cNeutral = (*ConditionGroup)(pkNeutral.PublicKey())
|
||||||
|
|
||||||
|
res, err := cGood.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, res)
|
||||||
|
|
||||||
|
res, err = cNeutral.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, res)
|
||||||
|
|
||||||
|
_, err = cBad.Match(tmc)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
t.Run("called by entry", func(t *testing.T) {
|
||||||
|
var c = ConditionCalledByEntry{}
|
||||||
|
|
||||||
|
res, err := c.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, res)
|
||||||
|
|
||||||
|
tmc2 := *tmc
|
||||||
|
tmc2.entry = util.Uint160{0, 9, 8}
|
||||||
|
res, err = c.Match(&tmc2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, res)
|
||||||
|
|
||||||
|
tmc3 := *tmc
|
||||||
|
tmc3.calling = util.Uint160{}
|
||||||
|
tmc3.current = tmc3.entry
|
||||||
|
res, err = c.Match(&tmc3)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, res)
|
||||||
|
})
|
||||||
|
t.Run("called by contract", func(t *testing.T) {
|
||||||
|
var cEntry = (*ConditionCalledByContract)(&entrySC)
|
||||||
|
var cCurrent = (*ConditionCalledByContract)(¤tSC)
|
||||||
|
|
||||||
|
res, err := cEntry.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, res)
|
||||||
|
res, err = cCurrent.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, res)
|
||||||
|
})
|
||||||
|
t.Run("called by group", func(t *testing.T) {
|
||||||
|
var cBad = (*ConditionCalledByGroup)(pkBad.PublicKey())
|
||||||
|
var cGood = (*ConditionCalledByGroup)(pkGood.PublicKey())
|
||||||
|
var cNeutral = (*ConditionCalledByGroup)(pkNeutral.PublicKey())
|
||||||
|
|
||||||
|
res, err := cGood.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, res)
|
||||||
|
|
||||||
|
res, err = cNeutral.Match(tmc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, res)
|
||||||
|
|
||||||
|
_, err = cBad.Match(tmc)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
86
pkg/core/transaction/witness_rule.go
Normal file
86
pkg/core/transaction/witness_rule.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package transaction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate stringer -type=WitnessAction -linecomment
|
||||||
|
|
||||||
|
// WitnessAction represents an action to perform in WitnessRule if
|
||||||
|
// WitnessCondition matches.
|
||||||
|
type WitnessAction byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
// WitnessDeny rejects current witness if condition is met.
|
||||||
|
WitnessDeny WitnessAction = 0 // Deny
|
||||||
|
// WitnessAllow approves current witness if condition is met.
|
||||||
|
WitnessAllow WitnessAction = 1 // Allow
|
||||||
|
)
|
||||||
|
|
||||||
|
// WitnessRule represents a single rule for Rules witness scope.
|
||||||
|
type WitnessRule struct {
|
||||||
|
Action WitnessAction `json:"action"`
|
||||||
|
Condition WitnessCondition `json:"condition"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type witnessRuleAux struct {
|
||||||
|
Action string `json:"action"`
|
||||||
|
Condition json.RawMessage `json:"condition"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements Serializable interface.
|
||||||
|
func (w *WitnessRule) EncodeBinary(bw *io.BinWriter) {
|
||||||
|
bw.WriteB(byte(w.Action))
|
||||||
|
w.Condition.EncodeBinary(bw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinary implements Serializable interface.
|
||||||
|
func (w *WitnessRule) DecodeBinary(br *io.BinReader) {
|
||||||
|
w.Action = WitnessAction(br.ReadB())
|
||||||
|
if br.Err == nil && w.Action != WitnessDeny && w.Action != WitnessAllow {
|
||||||
|
br.Err = errors.New("unknown witness rule action")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Condition = DecodeBinaryCondition(br)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||||
|
func (w *WitnessRule) MarshalJSON() ([]byte, error) {
|
||||||
|
cond, err := w.Condition.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aux := &witnessRuleAux{
|
||||||
|
Action: w.Action.String(),
|
||||||
|
Condition: cond,
|
||||||
|
}
|
||||||
|
return json.Marshal(aux)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||||
|
func (w *WitnessRule) UnmarshalJSON(data []byte) error {
|
||||||
|
aux := &witnessRuleAux{}
|
||||||
|
err := json.Unmarshal(data, aux)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var action WitnessAction
|
||||||
|
switch aux.Action {
|
||||||
|
case WitnessDeny.String():
|
||||||
|
action = WitnessDeny
|
||||||
|
case WitnessAllow.String():
|
||||||
|
action = WitnessAllow
|
||||||
|
default:
|
||||||
|
return errors.New("unknown witness rule action")
|
||||||
|
}
|
||||||
|
cond, err := UnmarshalConditionJSON(aux.Condition)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.Action = action
|
||||||
|
w.Condition = cond
|
||||||
|
return nil
|
||||||
|
}
|
56
pkg/core/transaction/witness_rule_test.go
Normal file
56
pkg/core/transaction/witness_rule_test.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package transaction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWitnessRuleSerDes(t *testing.T) {
|
||||||
|
var b bool
|
||||||
|
expected := &WitnessRule{
|
||||||
|
Action: WitnessAllow,
|
||||||
|
Condition: (*ConditionBoolean)(&b),
|
||||||
|
}
|
||||||
|
actual := &WitnessRule{}
|
||||||
|
testserdes.EncodeDecodeBinary(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWitnessRuleSerDesBad(t *testing.T) {
|
||||||
|
var b bool
|
||||||
|
bad := &WitnessRule{
|
||||||
|
Action: 0xff,
|
||||||
|
Condition: (*ConditionBoolean)(&b),
|
||||||
|
}
|
||||||
|
badB, err := testserdes.EncodeBinary(bad)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = testserdes.DecodeBinary(badB, &WitnessRule{})
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWitnessRuleJSON(t *testing.T) {
|
||||||
|
var b bool
|
||||||
|
expected := &WitnessRule{
|
||||||
|
Action: WitnessDeny,
|
||||||
|
Condition: (*ConditionBoolean)(&b),
|
||||||
|
}
|
||||||
|
actual := &WitnessRule{}
|
||||||
|
testserdes.MarshalUnmarshalJSON(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWitnessRuleBadJSON(t *testing.T) {
|
||||||
|
var cases = []string{
|
||||||
|
`{}`,
|
||||||
|
`[]`,
|
||||||
|
`{"action":"Allow"}`,
|
||||||
|
`{"action":"Unknown","condition":{"type":"Boolean", "expression":true}}`,
|
||||||
|
`{"action":"Allow","condition":{"type":"Boolean", "expression":42}}`,
|
||||||
|
}
|
||||||
|
for i := range cases {
|
||||||
|
actual := &WitnessRule{}
|
||||||
|
err := json.Unmarshal([]byte(cases[i]), actual)
|
||||||
|
require.Errorf(t, err, "case %d, json %s", i, cases[i])
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,8 @@ const (
|
||||||
CustomContracts WitnessScope = 0x10
|
CustomContracts WitnessScope = 0x10
|
||||||
// CustomGroups define custom pubkey for group members.
|
// CustomGroups define custom pubkey for group members.
|
||||||
CustomGroups WitnessScope = 0x20
|
CustomGroups WitnessScope = 0x20
|
||||||
|
// Rules is a set of conditions with boolean operators.
|
||||||
|
Rules WitnessScope = 0x40
|
||||||
// Global allows this witness in all contexts (default Neo2 behavior).
|
// Global allows this witness in all contexts (default Neo2 behavior).
|
||||||
// This cannot be combined with other flags.
|
// This cannot be combined with other flags.
|
||||||
Global WitnessScope = 0x80
|
Global WitnessScope = 0x80
|
||||||
|
@ -42,6 +44,7 @@ func ScopesFromString(s string) (WitnessScope, error) {
|
||||||
CalledByEntry.String(): CalledByEntry,
|
CalledByEntry.String(): CalledByEntry,
|
||||||
CustomContracts.String(): CustomContracts,
|
CustomContracts.String(): CustomContracts,
|
||||||
CustomGroups.String(): CustomGroups,
|
CustomGroups.String(): CustomGroups,
|
||||||
|
Rules.String(): Rules,
|
||||||
None.String(): None,
|
None.String(): None,
|
||||||
}
|
}
|
||||||
var isGlobal bool
|
var isGlobal bool
|
||||||
|
@ -61,6 +64,16 @@ func ScopesFromString(s string) (WitnessScope, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func appendScopeString(str string, scopes WitnessScope, scope WitnessScope) string {
|
||||||
|
if scopes&scope != 0 {
|
||||||
|
if len(str) != 0 {
|
||||||
|
str += ", "
|
||||||
|
}
|
||||||
|
str += scope.String()
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
// scopesToString converts witness scope to it's string representation. It uses
|
// scopesToString converts witness scope to it's string representation. It uses
|
||||||
// `, ` to separate scope names.
|
// `, ` to separate scope names.
|
||||||
func scopesToString(scopes WitnessScope) string {
|
func scopesToString(scopes WitnessScope) string {
|
||||||
|
@ -68,21 +81,10 @@ func scopesToString(scopes WitnessScope) string {
|
||||||
return scopes.String()
|
return scopes.String()
|
||||||
}
|
}
|
||||||
var res string
|
var res string
|
||||||
if scopes&CalledByEntry != 0 {
|
res = appendScopeString(res, scopes, CalledByEntry)
|
||||||
res = CalledByEntry.String()
|
res = appendScopeString(res, scopes, CustomContracts)
|
||||||
}
|
res = appendScopeString(res, scopes, CustomGroups)
|
||||||
if scopes&CustomContracts != 0 {
|
res = appendScopeString(res, scopes, Rules)
|
||||||
if len(res) != 0 {
|
|
||||||
res += ", "
|
|
||||||
}
|
|
||||||
res += CustomContracts.String()
|
|
||||||
}
|
|
||||||
if scopes&CustomGroups != 0 {
|
|
||||||
if len(res) != 0 {
|
|
||||||
res += ", "
|
|
||||||
}
|
|
||||||
res += CustomGroups.String()
|
|
||||||
}
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ func _() {
|
||||||
_ = x[CalledByEntry-1]
|
_ = x[CalledByEntry-1]
|
||||||
_ = x[CustomContracts-16]
|
_ = x[CustomContracts-16]
|
||||||
_ = x[CustomGroups-32]
|
_ = x[CustomGroups-32]
|
||||||
|
_ = x[Rules-64]
|
||||||
_ = x[Global-128]
|
_ = x[Global-128]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +20,8 @@ const (
|
||||||
_WitnessScope_name_0 = "NoneCalledByEntry"
|
_WitnessScope_name_0 = "NoneCalledByEntry"
|
||||||
_WitnessScope_name_1 = "CustomContracts"
|
_WitnessScope_name_1 = "CustomContracts"
|
||||||
_WitnessScope_name_2 = "CustomGroups"
|
_WitnessScope_name_2 = "CustomGroups"
|
||||||
_WitnessScope_name_3 = "Global"
|
_WitnessScope_name_3 = "Rules"
|
||||||
|
_WitnessScope_name_4 = "Global"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -28,14 +30,16 @@ var (
|
||||||
|
|
||||||
func (i WitnessScope) String() string {
|
func (i WitnessScope) String() string {
|
||||||
switch {
|
switch {
|
||||||
case i <= 1:
|
case 0 <= i && i <= 1:
|
||||||
return _WitnessScope_name_0[_WitnessScope_index_0[i]:_WitnessScope_index_0[i+1]]
|
return _WitnessScope_name_0[_WitnessScope_index_0[i]:_WitnessScope_index_0[i+1]]
|
||||||
case i == 16:
|
case i == 16:
|
||||||
return _WitnessScope_name_1
|
return _WitnessScope_name_1
|
||||||
case i == 32:
|
case i == 32:
|
||||||
return _WitnessScope_name_2
|
return _WitnessScope_name_2
|
||||||
case i == 128:
|
case i == 64:
|
||||||
return _WitnessScope_name_3
|
return _WitnessScope_name_3
|
||||||
|
case i == 128:
|
||||||
|
return _WitnessScope_name_4
|
||||||
default:
|
default:
|
||||||
return "WitnessScope(" + strconv.FormatInt(int64(i), 10) + ")"
|
return "WitnessScope(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
}
|
}
|
||||||
|
|
24
pkg/core/transaction/witnessaction_string.go
Normal file
24
pkg/core/transaction/witnessaction_string.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// Code generated by "stringer -type=WitnessAction -linecomment"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package transaction
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[WitnessDeny-0]
|
||||||
|
_ = x[WitnessAllow-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _WitnessAction_name = "DenyAllow"
|
||||||
|
|
||||||
|
var _WitnessAction_index = [...]uint8{0, 4, 9}
|
||||||
|
|
||||||
|
func (i WitnessAction) String() string {
|
||||||
|
if i >= WitnessAction(len(_WitnessAction_index)-1) {
|
||||||
|
return "WitnessAction(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _WitnessAction_name[_WitnessAction_index[i]:_WitnessAction_index[i+1]]
|
||||||
|
}
|
50
pkg/core/transaction/witnessconditiontype_string.go
Normal file
50
pkg/core/transaction/witnessconditiontype_string.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// Code generated by "stringer -type=WitnessConditionType -linecomment"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package transaction
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[WitnessBoolean-0]
|
||||||
|
_ = x[WitnessNot-1]
|
||||||
|
_ = x[WitnessAnd-2]
|
||||||
|
_ = x[WitnessOr-3]
|
||||||
|
_ = x[WitnessScriptHash-24]
|
||||||
|
_ = x[WitnessGroup-25]
|
||||||
|
_ = x[WitnessCalledByEntry-32]
|
||||||
|
_ = x[WitnessCalledByContract-40]
|
||||||
|
_ = x[WitnessCalledByGroup-41]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_WitnessConditionType_name_0 = "BooleanNotAndOr"
|
||||||
|
_WitnessConditionType_name_1 = "ScriptHashGroup"
|
||||||
|
_WitnessConditionType_name_2 = "CalledByEntry"
|
||||||
|
_WitnessConditionType_name_3 = "CalledByContractCalledByGroup"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_WitnessConditionType_index_0 = [...]uint8{0, 7, 10, 13, 15}
|
||||||
|
_WitnessConditionType_index_1 = [...]uint8{0, 10, 15}
|
||||||
|
_WitnessConditionType_index_3 = [...]uint8{0, 16, 29}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i WitnessConditionType) String() string {
|
||||||
|
switch {
|
||||||
|
case 0 <= i && i <= 3:
|
||||||
|
return _WitnessConditionType_name_0[_WitnessConditionType_index_0[i]:_WitnessConditionType_index_0[i+1]]
|
||||||
|
case 24 <= i && i <= 25:
|
||||||
|
i -= 24
|
||||||
|
return _WitnessConditionType_name_1[_WitnessConditionType_index_1[i]:_WitnessConditionType_index_1[i+1]]
|
||||||
|
case i == 32:
|
||||||
|
return _WitnessConditionType_name_2
|
||||||
|
case 40 <= i && i <= 41:
|
||||||
|
i -= 40
|
||||||
|
return _WitnessConditionType_name_3[_WitnessConditionType_index_3[i]:_WitnessConditionType_index_3[i+1]]
|
||||||
|
default:
|
||||||
|
return "WitnessConditionType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,6 +64,15 @@ func (g Groups) AreValid(h util.Uint160) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g Groups) Contains(k *keys.PublicKey) bool {
|
||||||
|
for i := range g {
|
||||||
|
if k.Equal(g[i].PublicKey) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler interface.
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
func (g *Group) MarshalJSON() ([]byte, error) {
|
func (g *Group) MarshalJSON() ([]byte, error) {
|
||||||
aux := &groupAux{
|
aux := &groupAux{
|
||||||
|
|
|
@ -41,3 +41,17 @@ func TestGroupsAreValid(t *testing.T) {
|
||||||
gps = Groups{gcorrect, gcorrect}
|
gps = Groups{gcorrect, gcorrect}
|
||||||
require.Error(t, gps.AreValid(h))
|
require.Error(t, gps.AreValid(h))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGroupsContains(t *testing.T) {
|
||||||
|
priv, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
priv2, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
priv3, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
g1 := Group{priv.PublicKey(), nil}
|
||||||
|
g2 := Group{priv2.PublicKey(), nil}
|
||||||
|
gps := Groups{g1, g2}
|
||||||
|
require.True(t, gps.Contains(priv2.PublicKey()))
|
||||||
|
require.False(t, gps.Contains(priv3.PublicKey()))
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue