Merge pull request #3407 from nspcc-dev/copy-transaction
*: add Copy() to transaction.Transaction and payload.P2PNotaryRequest
This commit is contained in:
commit
b54fcbcdd9
22 changed files with 526 additions and 29 deletions
|
@ -8,17 +8,22 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AttrValue represents a Transaction Attribute value.
|
||||||
|
type AttrValue interface {
|
||||||
|
io.Serializable
|
||||||
|
// toJSONMap is used for embedded json struct marshalling.
|
||||||
|
// Anonymous interface fields are not considered anonymous by
|
||||||
|
// json lib and marshaling Value together with type makes code
|
||||||
|
// harder to follow.
|
||||||
|
toJSONMap(map[string]any)
|
||||||
|
// Copy returns a deep copy of the attribute value.
|
||||||
|
Copy() AttrValue
|
||||||
|
}
|
||||||
|
|
||||||
// Attribute represents a Transaction attribute.
|
// Attribute represents a Transaction attribute.
|
||||||
type Attribute struct {
|
type Attribute struct {
|
||||||
Type AttrType
|
Type AttrType
|
||||||
Value interface {
|
Value AttrValue
|
||||||
io.Serializable
|
|
||||||
// toJSONMap is used for embedded json struct marshalling.
|
|
||||||
// Anonymous interface fields are not considered anonymous by
|
|
||||||
// json lib and marshaling Value together with type makes code
|
|
||||||
// harder to follow.
|
|
||||||
toJSONMap(map[string]any)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// attrJSON is used for JSON I/O of Attribute.
|
// attrJSON is used for JSON I/O of Attribute.
|
||||||
|
@ -107,3 +112,17 @@ func (attr *Attribute) UnmarshalJSON(data []byte) error {
|
||||||
}
|
}
|
||||||
return json.Unmarshal(data, attr.Value)
|
return json.Unmarshal(data, attr.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy creates a deep copy of the Attribute.
|
||||||
|
func (attr *Attribute) Copy() *Attribute {
|
||||||
|
if attr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cp := &Attribute{
|
||||||
|
Type: attr.Type,
|
||||||
|
}
|
||||||
|
if attr.Value != nil {
|
||||||
|
cp.Value = attr.Value.Copy()
|
||||||
|
}
|
||||||
|
return cp
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -177,3 +178,39 @@ func TestAttribute_MarshalJSON(t *testing.T) {
|
||||||
testserdes.MarshalUnmarshalJSON(t, attr, new(Attribute))
|
testserdes.MarshalUnmarshalJSON(t, attr, new(Attribute))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAttribute_Copy(t *testing.T) {
|
||||||
|
originals := []*Attribute{
|
||||||
|
{Type: HighPriority},
|
||||||
|
{Type: OracleResponseT, Value: &OracleResponse{ID: 123, Code: Success, Result: []byte{1, 2, 3}}},
|
||||||
|
{Type: NotValidBeforeT, Value: &NotValidBefore{Height: 123}},
|
||||||
|
{Type: ConflictsT, Value: &Conflicts{Hash: random.Uint256()}},
|
||||||
|
{Type: NotaryAssistedT, Value: &NotaryAssisted{NKeys: 3}},
|
||||||
|
{Type: ReservedLowerBound, Value: &Reserved{Value: []byte{1, 2, 3, 4, 5}}},
|
||||||
|
{Type: ReservedUpperBound, Value: &Reserved{Value: []byte{1, 2, 3, 4, 5}}},
|
||||||
|
{Type: ReservedLowerBound + 5, Value: &Reserved{Value: []byte{1, 2, 3, 4, 5}}},
|
||||||
|
}
|
||||||
|
require.Nil(t, (*Attribute)(nil).Copy())
|
||||||
|
|
||||||
|
for _, original := range originals {
|
||||||
|
cp := original.Copy()
|
||||||
|
require.NotNil(t, cp)
|
||||||
|
assert.Equal(t, original.Type, cp.Type)
|
||||||
|
assert.Equal(t, original.Value, cp.Value)
|
||||||
|
if original.Value != nil {
|
||||||
|
originalValueCopy := original.Value.Copy()
|
||||||
|
switch originalVal := originalValueCopy.(type) {
|
||||||
|
case *OracleResponse:
|
||||||
|
originalVal.Result = append(originalVal.Result, 0xFF)
|
||||||
|
assert.NotEqual(t, len(original.Value.(*OracleResponse).Result), len(originalVal.Result))
|
||||||
|
case *Conflicts:
|
||||||
|
originalVal.Hash = random.Uint256()
|
||||||
|
assert.NotEqual(t, original.Value.(*Conflicts).Hash, originalVal.Hash)
|
||||||
|
case *NotaryAssisted:
|
||||||
|
originalVal.NKeys++
|
||||||
|
assert.NotEqual(t, original.Value.(*NotaryAssisted).NKeys, originalVal.NKeys)
|
||||||
|
}
|
||||||
|
assert.Equal(t, cp.Value, original.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,3 +23,10 @@ func (c *Conflicts) EncodeBinary(w *io.BinWriter) {
|
||||||
func (c *Conflicts) toJSONMap(m map[string]any) {
|
func (c *Conflicts) toJSONMap(m map[string]any) {
|
||||||
m["hash"] = c.Hash
|
m["hash"] = c.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the AttrValue interface.
|
||||||
|
func (c *Conflicts) Copy() AttrValue {
|
||||||
|
return &Conflicts{
|
||||||
|
Hash: c.Hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,3 +22,10 @@ func (n *NotValidBefore) EncodeBinary(w *io.BinWriter) {
|
||||||
func (n *NotValidBefore) toJSONMap(m map[string]any) {
|
func (n *NotValidBefore) toJSONMap(m map[string]any) {
|
||||||
m["height"] = n.Height
|
m["height"] = n.Height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the AttrValue interface.
|
||||||
|
func (n *NotValidBefore) Copy() AttrValue {
|
||||||
|
return &NotValidBefore{
|
||||||
|
Height: n.Height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,3 +22,10 @@ func (n *NotaryAssisted) EncodeBinary(w *io.BinWriter) {
|
||||||
func (n *NotaryAssisted) toJSONMap(m map[string]any) {
|
func (n *NotaryAssisted) toJSONMap(m map[string]any) {
|
||||||
m["nkeys"] = n.NKeys
|
m["nkeys"] = n.NKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the AttrValue interface.
|
||||||
|
func (n *NotaryAssisted) Copy() AttrValue {
|
||||||
|
return &NotaryAssisted{
|
||||||
|
NKeys: n.NKeys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package transaction
|
package transaction
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
"math"
|
||||||
|
@ -116,3 +117,12 @@ func (r *OracleResponse) toJSONMap(m map[string]any) {
|
||||||
m["code"] = r.Code
|
m["code"] = r.Code
|
||||||
m["result"] = r.Result
|
m["result"] = r.Result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the AttrValue interface.
|
||||||
|
func (r *OracleResponse) Copy() AttrValue {
|
||||||
|
return &OracleResponse{
|
||||||
|
ID: r.ID,
|
||||||
|
Code: r.Code,
|
||||||
|
Result: bytes.Clone(r.Result),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,3 +22,10 @@ func (e *Reserved) EncodeBinary(w *io.BinWriter) {
|
||||||
func (e *Reserved) toJSONMap(m map[string]any) {
|
func (e *Reserved) toJSONMap(m map[string]any) {
|
||||||
m["value"] = e.Value
|
m["value"] = e.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the AttrValue interface.
|
||||||
|
func (e *Reserved) Copy() AttrValue {
|
||||||
|
return &Reserved{
|
||||||
|
Value: e.Value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -86,3 +86,24 @@ func SignersToStackItem(signers []Signer) stackitem.Item {
|
||||||
}
|
}
|
||||||
return stackitem.NewArray(res)
|
return stackitem.NewArray(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy creates a deep copy of the Signer.
|
||||||
|
func (c *Signer) Copy() *Signer {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cp := *c
|
||||||
|
if c.AllowedContracts != nil {
|
||||||
|
cp.AllowedContracts = make([]util.Uint160, len(c.AllowedContracts))
|
||||||
|
copy(cp.AllowedContracts, c.AllowedContracts)
|
||||||
|
}
|
||||||
|
cp.AllowedGroups = keys.PublicKeys(c.AllowedGroups).Copy()
|
||||||
|
if c.Rules != nil {
|
||||||
|
cp.Rules = make([]WitnessRule, len(c.Rules))
|
||||||
|
for i, rule := range c.Rules {
|
||||||
|
cp.Rules[i] = *rule.Copy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cp
|
||||||
|
}
|
||||||
|
|
|
@ -32,3 +32,40 @@ func TestCosignerMarshallUnmarshallJSON(t *testing.T) {
|
||||||
actual := &Signer{}
|
actual := &Signer{}
|
||||||
testserdes.MarshalUnmarshalJSON(t, expected, actual)
|
testserdes.MarshalUnmarshalJSON(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSignerCopy(t *testing.T) {
|
||||||
|
pk, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Nil(t, (*Signer)(nil).Copy())
|
||||||
|
|
||||||
|
original := &Signer{
|
||||||
|
Account: util.Uint160{1, 2, 3, 4, 5},
|
||||||
|
Scopes: CustomContracts | CustomGroups | Rules,
|
||||||
|
AllowedContracts: []util.Uint160{{1, 2, 3, 4}, {6, 7, 8, 9}},
|
||||||
|
AllowedGroups: keys.PublicKeys{pk.PublicKey()},
|
||||||
|
Rules: []WitnessRule{{Action: WitnessAllow, Condition: ConditionCalledByEntry{}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
cp := original.Copy()
|
||||||
|
require.NotNil(t, cp, "Copied Signer should not be nil")
|
||||||
|
|
||||||
|
require.Equal(t, original.Account, cp.Account)
|
||||||
|
require.Equal(t, original.Scopes, cp.Scopes)
|
||||||
|
|
||||||
|
require.NotSame(t, original.AllowedContracts, cp.AllowedContracts)
|
||||||
|
require.Equal(t, original.AllowedContracts, cp.AllowedContracts)
|
||||||
|
|
||||||
|
require.NotSame(t, original.AllowedGroups, cp.AllowedGroups)
|
||||||
|
require.Equal(t, original.AllowedGroups, cp.AllowedGroups)
|
||||||
|
|
||||||
|
require.NotSame(t, original.Rules, cp.Rules)
|
||||||
|
require.Equal(t, original.Rules, cp.Rules)
|
||||||
|
|
||||||
|
original.AllowedContracts[0][0] = 255
|
||||||
|
original.AllowedGroups[0] = nil
|
||||||
|
original.Rules[0].Action = WitnessDeny
|
||||||
|
|
||||||
|
require.NotEqual(t, original.AllowedContracts[0][0], cp.AllowedContracts[0][0])
|
||||||
|
require.NotEqual(t, original.AllowedGroups[0], cp.AllowedGroups[0])
|
||||||
|
require.NotEqual(t, original.Rules[0].Action, cp.Rules[0].Action)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package transaction
|
package transaction
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -468,3 +469,36 @@ func (t *Transaction) ToStackItem() stackitem.Item {
|
||||||
stackitem.NewByteArray(t.Script),
|
stackitem.NewByteArray(t.Script),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy creates a deep copy of the Transaction, including all slice fields. Cached values like
|
||||||
|
// 'hashed' and 'size' are reset to ensure the copy can be modified independently of the original.
|
||||||
|
func (t *Transaction) Copy() *Transaction {
|
||||||
|
if t == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cp := *t
|
||||||
|
if t.Attributes != nil {
|
||||||
|
cp.Attributes = make([]Attribute, len(t.Attributes))
|
||||||
|
for i, attr := range t.Attributes {
|
||||||
|
cp.Attributes[i] = *attr.Copy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t.Signers != nil {
|
||||||
|
cp.Signers = make([]Signer, len(t.Signers))
|
||||||
|
for i, signer := range t.Signers {
|
||||||
|
cp.Signers[i] = *signer.Copy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t.Scripts != nil {
|
||||||
|
cp.Scripts = make([]Witness, len(t.Scripts))
|
||||||
|
for i, script := range t.Scripts {
|
||||||
|
cp.Scripts[i] = script.Copy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cp.Script = bytes.Clone(t.Script)
|
||||||
|
|
||||||
|
cp.hashed = false
|
||||||
|
cp.size = 0
|
||||||
|
cp.hash = util.Uint256{}
|
||||||
|
return &cp
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
@ -321,3 +322,68 @@ func BenchmarkTxHash(b *testing.B) {
|
||||||
_ = tx.Hash()
|
_ = tx.Hash()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTransaction_DeepCopy(t *testing.T) {
|
||||||
|
origTx := New([]byte{0x01, 0x02, 0x03}, 1000)
|
||||||
|
origTx.NetworkFee = 2000
|
||||||
|
origTx.SystemFee = 500
|
||||||
|
origTx.Nonce = 12345678
|
||||||
|
origTx.ValidUntilBlock = 100
|
||||||
|
origTx.Version = 1
|
||||||
|
require.Nil(t, (*Transaction)(nil).Copy())
|
||||||
|
|
||||||
|
priv, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
origTx.Signers = []Signer{
|
||||||
|
{Account: random.Uint160(), Scopes: Global, AllowedContracts: []util.Uint160{random.Uint160()}, AllowedGroups: keys.PublicKeys{priv.PublicKey()}, Rules: []WitnessRule{{Action: 0x01, Condition: ConditionCalledByEntry{}}}},
|
||||||
|
{Account: random.Uint160(), Scopes: CalledByEntry},
|
||||||
|
}
|
||||||
|
origTx.Attributes = []Attribute{
|
||||||
|
{Type: HighPriority, Value: &OracleResponse{
|
||||||
|
ID: 0,
|
||||||
|
Code: Success,
|
||||||
|
Result: []byte{4, 8, 15, 16, 23, 42},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
origTx.Scripts = []Witness{
|
||||||
|
{
|
||||||
|
InvocationScript: []byte{0x04, 0x05},
|
||||||
|
VerificationScript: []byte{0x06, 0x07},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
origTxHash := origTx.Hash()
|
||||||
|
|
||||||
|
copyTx := origTx.Copy()
|
||||||
|
|
||||||
|
require.Equal(t, origTx.Hash(), copyTx.Hash())
|
||||||
|
require.Equal(t, origTx, copyTx)
|
||||||
|
require.Equal(t, origTx.Size(), copyTx.Size())
|
||||||
|
|
||||||
|
copyTx.NetworkFee = 3000
|
||||||
|
copyTx.Signers[0].Scopes = None
|
||||||
|
copyTx.Attributes[0].Type = NotaryAssistedT
|
||||||
|
copyTx.Scripts[0].InvocationScript[0] = 0x08
|
||||||
|
copyTx.hashed = false
|
||||||
|
modifiedCopyTxHash := copyTx.Hash()
|
||||||
|
|
||||||
|
require.NotEqual(t, origTx.NetworkFee, copyTx.NetworkFee)
|
||||||
|
require.NotEqual(t, origTx.Signers[0].Scopes, copyTx.Signers[0].Scopes)
|
||||||
|
require.NotEqual(t, origTx.Attributes, copyTx.Attributes)
|
||||||
|
require.NotEqual(t, origTxHash, modifiedCopyTxHash)
|
||||||
|
|
||||||
|
require.NotEqual(t, &origTx.Scripts[0].InvocationScript[0], ©Tx.Scripts[0].InvocationScript[0])
|
||||||
|
require.NotEqual(t, &origTx.Scripts, ©Tx.Scripts)
|
||||||
|
require.Equal(t, origTx.Scripts[0].VerificationScript, copyTx.Scripts[0].VerificationScript)
|
||||||
|
require.Equal(t, origTx.Signers[0].AllowedContracts, copyTx.Signers[0].AllowedContracts)
|
||||||
|
require.Equal(t, origTx.Signers[0].AllowedGroups, copyTx.Signers[0].AllowedGroups)
|
||||||
|
origGroup := origTx.Signers[0].AllowedGroups[0]
|
||||||
|
copyGroup := copyTx.Signers[0].AllowedGroups[0]
|
||||||
|
require.True(t, origGroup.Equal(copyGroup))
|
||||||
|
|
||||||
|
copyTx.Signers[0].AllowedGroups[0] = nil
|
||||||
|
require.NotEqual(t, origTx.Signers[0].AllowedGroups[0], copyTx.Signers[0].AllowedGroups[0])
|
||||||
|
|
||||||
|
require.Equal(t, origTx.Signers[0].Rules[0], copyTx.Signers[0].Rules[0])
|
||||||
|
copyTx.Signers[0].Rules[0].Action = 0x02
|
||||||
|
require.NotEqual(t, origTx.Signers[0].Rules[0].Action, copyTx.Signers[0].Rules[0].Action)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package transaction
|
package transaction
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
"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/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
@ -38,3 +40,11 @@ func (w *Witness) EncodeBinary(bw *io.BinWriter) {
|
||||||
func (w Witness) ScriptHash() util.Uint160 {
|
func (w Witness) ScriptHash() util.Uint160 {
|
||||||
return hash.Hash160(w.VerificationScript)
|
return hash.Hash160(w.VerificationScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy creates a deep copy of the Witness.
|
||||||
|
func (w Witness) Copy() Witness {
|
||||||
|
return Witness{
|
||||||
|
InvocationScript: bytes.Clone(w.InvocationScript),
|
||||||
|
VerificationScript: bytes.Clone(w.VerificationScript),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -54,6 +54,8 @@ type WitnessCondition interface {
|
||||||
DecodeBinarySpecific(*io.BinReader, int)
|
DecodeBinarySpecific(*io.BinReader, int)
|
||||||
// ToStackItem converts WitnessCondition to stackitem.Item.
|
// ToStackItem converts WitnessCondition to stackitem.Item.
|
||||||
ToStackItem() stackitem.Item
|
ToStackItem() stackitem.Item
|
||||||
|
// Copy returns a deep copy of the condition.
|
||||||
|
Copy() WitnessCondition
|
||||||
|
|
||||||
json.Marshaler
|
json.Marshaler
|
||||||
}
|
}
|
||||||
|
@ -139,6 +141,12 @@ func (c *ConditionBoolean) ToStackItem() stackitem.Item {
|
||||||
return condToStackItem(c.Type(), bool(*c))
|
return condToStackItem(c.Type(), bool(*c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy returns a deep copy of the condition.
|
||||||
|
func (c *ConditionBoolean) Copy() WitnessCondition {
|
||||||
|
cc := *c
|
||||||
|
return &cc
|
||||||
|
}
|
||||||
|
|
||||||
// Type implements the WitnessCondition interface and returns condition type.
|
// Type implements the WitnessCondition interface and returns condition type.
|
||||||
func (c *ConditionNot) Type() WitnessConditionType {
|
func (c *ConditionNot) Type() WitnessConditionType {
|
||||||
return WitnessNot
|
return WitnessNot
|
||||||
|
@ -182,6 +190,12 @@ func (c *ConditionNot) ToStackItem() stackitem.Item {
|
||||||
return condToStackItem(c.Type(), c.Condition)
|
return condToStackItem(c.Type(), c.Condition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the WitnessCondition interface and returns a deep copy of the condition.
|
||||||
|
func (c *ConditionNot) Copy() WitnessCondition {
|
||||||
|
cp := *c
|
||||||
|
return &cp
|
||||||
|
}
|
||||||
|
|
||||||
// Type implements the WitnessCondition interface and returns condition type.
|
// Type implements the WitnessCondition interface and returns condition type.
|
||||||
func (c *ConditionAnd) Type() WitnessConditionType {
|
func (c *ConditionAnd) Type() WitnessConditionType {
|
||||||
return WitnessAnd
|
return WitnessAnd
|
||||||
|
@ -264,6 +278,15 @@ func (c *ConditionAnd) ToStackItem() stackitem.Item {
|
||||||
return condToStackItem(c.Type(), []WitnessCondition(*c))
|
return condToStackItem(c.Type(), []WitnessCondition(*c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the WitnessCondition interface and returns a deep copy of the condition.
|
||||||
|
func (c *ConditionAnd) Copy() WitnessCondition {
|
||||||
|
cp := make(ConditionAnd, len(*c))
|
||||||
|
for i, cond := range *c {
|
||||||
|
cp[i] = cond.Copy()
|
||||||
|
}
|
||||||
|
return &cp
|
||||||
|
}
|
||||||
|
|
||||||
// Type implements the WitnessCondition interface and returns condition type.
|
// Type implements the WitnessCondition interface and returns condition type.
|
||||||
func (c *ConditionOr) Type() WitnessConditionType {
|
func (c *ConditionOr) Type() WitnessConditionType {
|
||||||
return WitnessOr
|
return WitnessOr
|
||||||
|
@ -310,6 +333,15 @@ func (c *ConditionOr) ToStackItem() stackitem.Item {
|
||||||
return condToStackItem(c.Type(), []WitnessCondition(*c))
|
return condToStackItem(c.Type(), []WitnessCondition(*c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the WitnessCondition interface and returns a deep copy of the condition.
|
||||||
|
func (c *ConditionOr) Copy() WitnessCondition {
|
||||||
|
cp := make(ConditionOr, len(*c))
|
||||||
|
for i, cond := range *c {
|
||||||
|
cp[i] = cond.Copy()
|
||||||
|
}
|
||||||
|
return &cp
|
||||||
|
}
|
||||||
|
|
||||||
// Type implements the WitnessCondition interface and returns condition type.
|
// Type implements the WitnessCondition interface and returns condition type.
|
||||||
func (c *ConditionScriptHash) Type() WitnessConditionType {
|
func (c *ConditionScriptHash) Type() WitnessConditionType {
|
||||||
return WitnessScriptHash
|
return WitnessScriptHash
|
||||||
|
@ -348,6 +380,12 @@ func (c *ConditionScriptHash) ToStackItem() stackitem.Item {
|
||||||
return condToStackItem(c.Type(), util.Uint160(*c))
|
return condToStackItem(c.Type(), util.Uint160(*c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the WitnessCondition interface and returns a deep copy of the condition.
|
||||||
|
func (c *ConditionScriptHash) Copy() WitnessCondition {
|
||||||
|
cc := *c
|
||||||
|
return &cc
|
||||||
|
}
|
||||||
|
|
||||||
// Type implements the WitnessCondition interface and returns condition type.
|
// Type implements the WitnessCondition interface and returns condition type.
|
||||||
func (c *ConditionGroup) Type() WitnessConditionType {
|
func (c *ConditionGroup) Type() WitnessConditionType {
|
||||||
return WitnessGroup
|
return WitnessGroup
|
||||||
|
@ -386,6 +424,12 @@ func (c *ConditionGroup) ToStackItem() stackitem.Item {
|
||||||
return condToStackItem(c.Type(), keys.PublicKey(*c))
|
return condToStackItem(c.Type(), keys.PublicKey(*c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the WitnessCondition interface and returns a deep copy of the condition.
|
||||||
|
func (c *ConditionGroup) Copy() WitnessCondition {
|
||||||
|
cp := *c
|
||||||
|
return &cp
|
||||||
|
}
|
||||||
|
|
||||||
// Type implements the WitnessCondition interface and returns condition type.
|
// Type implements the WitnessCondition interface and returns condition type.
|
||||||
func (c ConditionCalledByEntry) Type() WitnessConditionType {
|
func (c ConditionCalledByEntry) Type() WitnessConditionType {
|
||||||
return WitnessCalledByEntry
|
return WitnessCalledByEntry
|
||||||
|
@ -421,6 +465,11 @@ func (c ConditionCalledByEntry) ToStackItem() stackitem.Item {
|
||||||
return condToStackItem(c.Type(), nil)
|
return condToStackItem(c.Type(), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the WitnessCondition interface and returns a deep copy of the condition.
|
||||||
|
func (c ConditionCalledByEntry) Copy() WitnessCondition {
|
||||||
|
return ConditionCalledByEntry{}
|
||||||
|
}
|
||||||
|
|
||||||
// Type implements the WitnessCondition interface and returns condition type.
|
// Type implements the WitnessCondition interface and returns condition type.
|
||||||
func (c *ConditionCalledByContract) Type() WitnessConditionType {
|
func (c *ConditionCalledByContract) Type() WitnessConditionType {
|
||||||
return WitnessCalledByContract
|
return WitnessCalledByContract
|
||||||
|
@ -459,6 +508,12 @@ func (c *ConditionCalledByContract) ToStackItem() stackitem.Item {
|
||||||
return condToStackItem(c.Type(), util.Uint160(*c))
|
return condToStackItem(c.Type(), util.Uint160(*c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the WitnessCondition interface and returns a deep copy of the condition.
|
||||||
|
func (c *ConditionCalledByContract) Copy() WitnessCondition {
|
||||||
|
cc := *c
|
||||||
|
return &cc
|
||||||
|
}
|
||||||
|
|
||||||
// Type implements the WitnessCondition interface and returns condition type.
|
// Type implements the WitnessCondition interface and returns condition type.
|
||||||
func (c *ConditionCalledByGroup) Type() WitnessConditionType {
|
func (c *ConditionCalledByGroup) Type() WitnessConditionType {
|
||||||
return WitnessCalledByGroup
|
return WitnessCalledByGroup
|
||||||
|
@ -497,6 +552,12 @@ func (c *ConditionCalledByGroup) ToStackItem() stackitem.Item {
|
||||||
return condToStackItem(c.Type(), keys.PublicKey(*c))
|
return condToStackItem(c.Type(), keys.PublicKey(*c))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the WitnessCondition interface and returns a deep copy of the condition.
|
||||||
|
func (c *ConditionCalledByGroup) Copy() WitnessCondition {
|
||||||
|
cp := *c
|
||||||
|
return &cp
|
||||||
|
}
|
||||||
|
|
||||||
// DecodeBinaryCondition decodes and returns condition from the given binary stream.
|
// DecodeBinaryCondition decodes and returns condition from the given binary stream.
|
||||||
func DecodeBinaryCondition(r *io.BinReader) WitnessCondition {
|
func DecodeBinaryCondition(r *io.BinReader) WitnessCondition {
|
||||||
return decodeBinaryCondition(r, MaxConditionNesting)
|
return decodeBinaryCondition(r, MaxConditionNesting)
|
||||||
|
|
|
@ -36,6 +36,11 @@ func (c InvalidCondition) ToStackItem() stackitem.Item {
|
||||||
panic("invalid")
|
panic("invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy implements the WitnessCondition interface and returns a deep copy of the condition.
|
||||||
|
func (c InvalidCondition) Copy() WitnessCondition {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
type condCase struct {
|
type condCase struct {
|
||||||
condition WitnessCondition
|
condition WitnessCondition
|
||||||
success bool
|
success bool
|
||||||
|
@ -347,3 +352,66 @@ func TestWitnessConditionMatch(t *testing.T) {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWitnessConditionCopy(t *testing.T) {
|
||||||
|
var someBool = true
|
||||||
|
boolCondition := (*ConditionBoolean)(&someBool)
|
||||||
|
pk, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
conditions := []WitnessCondition{
|
||||||
|
boolCondition,
|
||||||
|
&ConditionNot{Condition: boolCondition},
|
||||||
|
&ConditionAnd{boolCondition, boolCondition},
|
||||||
|
&ConditionOr{boolCondition, boolCondition},
|
||||||
|
&ConditionScriptHash{1, 2, 3},
|
||||||
|
(*ConditionGroup)(pk.PublicKey()),
|
||||||
|
ConditionCalledByEntry{},
|
||||||
|
&ConditionCalledByContract{1, 2, 3},
|
||||||
|
(*ConditionCalledByGroup)(pk.PublicKey()),
|
||||||
|
&ConditionNot{Condition: &ConditionNot{Condition: &ConditionNot{Condition: boolCondition}}},
|
||||||
|
}
|
||||||
|
for _, cond := range conditions {
|
||||||
|
copied := cond.Copy()
|
||||||
|
require.Equal(t, cond, copied)
|
||||||
|
|
||||||
|
switch c := copied.(type) {
|
||||||
|
case *ConditionBoolean:
|
||||||
|
require.NotSame(t, c, cond.(*ConditionBoolean))
|
||||||
|
case *ConditionScriptHash:
|
||||||
|
c[0]++
|
||||||
|
require.NotEqual(t, c, cond.(*ConditionScriptHash))
|
||||||
|
case *ConditionGroup:
|
||||||
|
c = (*ConditionGroup)(pk.PublicKey())
|
||||||
|
require.NotSame(t, c, cond.(*ConditionGroup))
|
||||||
|
newPk, _ := keys.NewPrivateKey()
|
||||||
|
copied = (*ConditionGroup)(newPk.PublicKey())
|
||||||
|
require.NotEqual(t, copied, cond)
|
||||||
|
case *ConditionCalledByContract:
|
||||||
|
c[0]++
|
||||||
|
require.NotEqual(t, c, cond.(*ConditionCalledByContract))
|
||||||
|
case *ConditionCalledByGroup:
|
||||||
|
c = (*ConditionCalledByGroup)(pk.PublicKey())
|
||||||
|
require.NotSame(t, c, cond.(*ConditionCalledByGroup))
|
||||||
|
newPk, _ := keys.NewPrivateKey()
|
||||||
|
copied = (*ConditionCalledByGroup)(newPk.PublicKey())
|
||||||
|
require.NotEqual(t, copied, cond)
|
||||||
|
case *ConditionNot:
|
||||||
|
require.NotSame(t, copied, cond)
|
||||||
|
copied.(*ConditionNot).Condition = ConditionCalledByEntry{}
|
||||||
|
require.NotEqual(t, copied, cond)
|
||||||
|
case *ConditionAnd:
|
||||||
|
require.NotSame(t, copied, cond)
|
||||||
|
(*(copied.(*ConditionAnd)))[0] = ConditionCalledByEntry{}
|
||||||
|
require.NotEqual(t, copied, cond)
|
||||||
|
case *ConditionOr:
|
||||||
|
require.NotSame(t, copied, cond)
|
||||||
|
(*(copied.(*ConditionOr)))[0] = ConditionCalledByEntry{}
|
||||||
|
require.NotEqual(t, copied, cond)
|
||||||
|
case *ConditionCalledByEntry:
|
||||||
|
require.NotSame(t, copied, cond)
|
||||||
|
copied = ConditionCalledByEntry{}
|
||||||
|
require.NotEqual(t, copied, cond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -94,3 +94,11 @@ func (w *WitnessRule) ToStackItem() stackitem.Item {
|
||||||
w.Condition.ToStackItem(),
|
w.Condition.ToStackItem(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy creates a deep copy of the WitnessRule.
|
||||||
|
func (w *WitnessRule) Copy() *WitnessRule {
|
||||||
|
return &WitnessRule{
|
||||||
|
Action: w.Action,
|
||||||
|
Condition: w.Condition.Copy(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -73,3 +73,15 @@ func TestWitnessRule_ToStackItem(t *testing.T) {
|
||||||
require.Equal(t, expected, actual, act)
|
require.Equal(t, expected, actual, act)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWitnessRule_Copy(t *testing.T) {
|
||||||
|
b := true
|
||||||
|
wr := &WitnessRule{
|
||||||
|
Action: WitnessDeny,
|
||||||
|
Condition: (*ConditionBoolean)(&b),
|
||||||
|
}
|
||||||
|
copied := wr.Copy()
|
||||||
|
require.Equal(t, wr.Action, copied.Action)
|
||||||
|
require.Equal(t, wr.Condition, copied.Condition)
|
||||||
|
require.NotSame(t, wr.Condition, copied.Condition)
|
||||||
|
}
|
||||||
|
|
|
@ -36,3 +36,16 @@ func TestWitnessSerDes(t *testing.T) {
|
||||||
require.Error(t, testserdes.DecodeBinary(bin1, exp))
|
require.Error(t, testserdes.DecodeBinary(bin1, exp))
|
||||||
require.Error(t, testserdes.DecodeBinary(bin2, exp))
|
require.Error(t, testserdes.DecodeBinary(bin2, exp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWitnessCopy(t *testing.T) {
|
||||||
|
original := &Witness{
|
||||||
|
InvocationScript: []byte{1, 2, 3},
|
||||||
|
VerificationScript: []byte{3, 2, 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
cp := original.Copy()
|
||||||
|
require.Equal(t, *original, cp)
|
||||||
|
|
||||||
|
original.InvocationScript[0] = 0x05
|
||||||
|
require.NotEqual(t, *original, cp)
|
||||||
|
}
|
||||||
|
|
|
@ -78,8 +78,12 @@ func (keys PublicKeys) Contains(pKey *PublicKey) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns a copy of keys.
|
// Copy returns a shallow copy of the PublicKeys slice. It creates a new slice with the same elements,
|
||||||
|
// but does not perform a deep copy of the elements themselves.
|
||||||
func (keys PublicKeys) Copy() PublicKeys {
|
func (keys PublicKeys) Copy() PublicKeys {
|
||||||
|
if keys == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
res := make(PublicKeys, len(keys))
|
res := make(PublicKeys, len(keys))
|
||||||
copy(res, keys)
|
copy(res, keys)
|
||||||
return res
|
return res
|
||||||
|
|
|
@ -40,14 +40,20 @@ func TestEncodeDecodePublicKey(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPublicKeys_Copy(t *testing.T) {
|
func TestPublicKeys_Copy(t *testing.T) {
|
||||||
pubs := make(PublicKeys, 5)
|
require.Nil(t, (PublicKeys)(nil).Copy())
|
||||||
for i := range pubs {
|
|
||||||
|
pubz := make([]*PublicKey, 5)
|
||||||
|
for i := range pubz {
|
||||||
priv, err := NewPrivateKey()
|
priv, err := NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
pubs[i] = priv.PublicKey()
|
pubz[i] = priv.PublicKey()
|
||||||
}
|
}
|
||||||
|
pubs := PublicKeys(pubz)
|
||||||
|
|
||||||
cp := pubs.Copy()
|
cp := pubs.Copy()
|
||||||
|
var pubx = ([]*PublicKey)(cp)
|
||||||
|
require.Equal(t, pubz, pubx)
|
||||||
|
|
||||||
priv, err := NewPrivateKey()
|
priv, err := NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
cp[0] = priv.PublicKey()
|
cp[0] = priv.PublicKey()
|
||||||
|
|
|
@ -136,3 +136,15 @@ func (r *P2PNotaryRequest) isValid() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy creates a deep copy of P2PNotaryRequest. It creates deep copy of the MainTransaction,
|
||||||
|
// FallbackTransaction and Witness, including all slice fields. Cached values like
|
||||||
|
// 'hashed' and 'size' of the transactions are reset to ensure the copy can be modified
|
||||||
|
// independently of the original.
|
||||||
|
func (r *P2PNotaryRequest) Copy() *P2PNotaryRequest {
|
||||||
|
return &P2PNotaryRequest{
|
||||||
|
MainTransaction: r.MainTransaction.Copy(),
|
||||||
|
FallbackTransaction: r.FallbackTransaction.Copy(),
|
||||||
|
Witness: r.Witness.Copy(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -186,3 +186,69 @@ func TestNotaryRequestBytesFromBytes(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, p, actual)
|
require.Equal(t, p, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestP2PNotaryRequest_Copy(t *testing.T) {
|
||||||
|
priv, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
orig := &P2PNotaryRequest{
|
||||||
|
MainTransaction: &transaction.Transaction{
|
||||||
|
NetworkFee: 2000,
|
||||||
|
SystemFee: 500,
|
||||||
|
Nonce: 12345678,
|
||||||
|
ValidUntilBlock: 100,
|
||||||
|
Version: 1,
|
||||||
|
Signers: []transaction.Signer{
|
||||||
|
{Account: random.Uint160(), Scopes: transaction.Global, AllowedContracts: []util.Uint160{random.Uint160()}, AllowedGroups: keys.PublicKeys{priv.PublicKey()}, Rules: []transaction.WitnessRule{{Action: 0x01, Condition: transaction.ConditionCalledByEntry{}}}},
|
||||||
|
{Account: random.Uint160(), Scopes: transaction.CalledByEntry},
|
||||||
|
},
|
||||||
|
|
||||||
|
Attributes: []transaction.Attribute{
|
||||||
|
{Type: transaction.HighPriority, Value: &transaction.OracleResponse{
|
||||||
|
ID: 0,
|
||||||
|
Code: transaction.Success,
|
||||||
|
Result: []byte{4, 8, 15, 16, 23, 42},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
Scripts: []transaction.Witness{
|
||||||
|
{
|
||||||
|
InvocationScript: []byte{0x04, 0x05},
|
||||||
|
VerificationScript: []byte{0x06, 0x07},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
FallbackTransaction: &transaction.Transaction{
|
||||||
|
Version: 2,
|
||||||
|
SystemFee: 200,
|
||||||
|
NetworkFee: 100,
|
||||||
|
Script: []byte{3, 2, 1},
|
||||||
|
Signers: []transaction.Signer{{Account: util.Uint160{4, 5, 6}}},
|
||||||
|
Attributes: []transaction.Attribute{{Type: transaction.NotValidBeforeT}},
|
||||||
|
Scripts: []transaction.Witness{
|
||||||
|
{
|
||||||
|
InvocationScript: []byte{0x0D, 0x0E},
|
||||||
|
VerificationScript: []byte{0x0F, 0x10},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Witness: transaction.Witness{
|
||||||
|
InvocationScript: []byte{0x11, 0x12},
|
||||||
|
VerificationScript: []byte{0x13, 0x14},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
p2pCopy := orig.Copy()
|
||||||
|
|
||||||
|
require.Equal(t, orig, p2pCopy)
|
||||||
|
require.NotSame(t, orig, p2pCopy)
|
||||||
|
|
||||||
|
require.Equal(t, orig.MainTransaction, p2pCopy.MainTransaction)
|
||||||
|
require.Equal(t, orig.FallbackTransaction, p2pCopy.FallbackTransaction)
|
||||||
|
require.Equal(t, orig.Witness, p2pCopy.Witness)
|
||||||
|
|
||||||
|
p2pCopy.MainTransaction.Version = 3
|
||||||
|
p2pCopy.FallbackTransaction.Script[0] = 0x1F
|
||||||
|
p2pCopy.Witness.VerificationScript[1] = 0x22
|
||||||
|
|
||||||
|
require.NotEqual(t, orig.MainTransaction.Version, p2pCopy.MainTransaction.Version)
|
||||||
|
require.NotEqual(t, orig.FallbackTransaction.Script[0], p2pCopy.FallbackTransaction.Script[0])
|
||||||
|
require.NotEqual(t, orig.Witness.VerificationScript[1], p2pCopy.Witness.VerificationScript[1])
|
||||||
|
}
|
||||||
|
|
|
@ -272,7 +272,7 @@ func (n *Notary) OnNewRequest(payload *payload.P2PNotaryRequest) {
|
||||||
// Avoid changes in the main transaction witnesses got from the notary request pool to
|
// Avoid changes in the main transaction witnesses got from the notary request pool to
|
||||||
// keep the pooled tx valid. We will update its copy => the copy's size will be changed.
|
// keep the pooled tx valid. We will update its copy => the copy's size will be changed.
|
||||||
r = &request{
|
r = &request{
|
||||||
main: safeCopy(payload.MainTransaction),
|
main: payload.MainTransaction.Copy(),
|
||||||
minNotValidBefore: nvbFallback,
|
minNotValidBefore: nvbFallback,
|
||||||
}
|
}
|
||||||
n.requests[payload.MainTransaction.Hash()] = r
|
n.requests[payload.MainTransaction.Hash()] = r
|
||||||
|
@ -285,7 +285,7 @@ func (n *Notary) OnNewRequest(payload *payload.P2PNotaryRequest) {
|
||||||
// size won't be changed after finalisation, the witness bytes changes may
|
// size won't be changed after finalisation, the witness bytes changes may
|
||||||
// affect the other users of notary pool and cause race. Avoid this by making
|
// affect the other users of notary pool and cause race. Avoid this by making
|
||||||
// the copy.
|
// the copy.
|
||||||
r.fallbacks = append(r.fallbacks, safeCopy(payload.FallbackTransaction))
|
r.fallbacks = append(r.fallbacks, payload.FallbackTransaction.Copy())
|
||||||
if exists && r.isMainCompleted() || validationErr != nil {
|
if exists && r.isMainCompleted() || validationErr != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -345,21 +345,6 @@ func (n *Notary) OnNewRequest(payload *payload.P2PNotaryRequest) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// safeCopy creates a copy of provided transaction by dereferencing it and creating
|
|
||||||
// fresh witnesses so that the tx's witnesses may be modified without affecting the
|
|
||||||
// copy's ones.
|
|
||||||
func safeCopy(tx *transaction.Transaction) *transaction.Transaction {
|
|
||||||
cp := *tx
|
|
||||||
cp.Scripts = make([]transaction.Witness, len(tx.Scripts))
|
|
||||||
for i := range cp.Scripts {
|
|
||||||
cp.Scripts[i] = transaction.Witness{
|
|
||||||
InvocationScript: bytes.Clone(tx.Scripts[i].InvocationScript),
|
|
||||||
VerificationScript: bytes.Clone(tx.Scripts[i].VerificationScript),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &cp
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnRequestRemoval is a callback which is called after fallback transaction is removed
|
// OnRequestRemoval is a callback which is called after fallback transaction is removed
|
||||||
// from the notary payload pool due to expiration, main tx appliance or any other reason.
|
// from the notary payload pool due to expiration, main tx appliance or any other reason.
|
||||||
func (n *Notary) OnRequestRemoval(pld *payload.P2PNotaryRequest) {
|
func (n *Notary) OnRequestRemoval(pld *payload.P2PNotaryRequest) {
|
||||||
|
|
Loading…
Reference in a new issue