smartcontract: implement ContextItem

This commit is contained in:
Evgenii Stratonikov 2020-03-04 12:40:01 +03:00
parent 46db4e9d9d
commit acea3867b2
2 changed files with 149 additions and 0 deletions

View file

@ -0,0 +1,75 @@
package context
import (
"encoding/hex"
"encoding/json"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Item represents a transaction context item.
type Item struct {
Script util.Uint160
Parameters []smartcontract.Parameter
Signatures map[string][]byte
}
type itemAux struct {
Script util.Uint160 `json:"script"`
Parameters []smartcontract.Parameter `json:"parameters"`
Signatures map[string]string `json:"signatures"`
}
// GetSignature returns signature for pub if present.
func (it *Item) GetSignature(pub *keys.PublicKey) []byte {
return it.Signatures[hex.EncodeToString(pub.Bytes())]
}
// AddSignature adds a signature for pub.
func (it *Item) AddSignature(pub *keys.PublicKey, sig []byte) {
pubHex := hex.EncodeToString(pub.Bytes())
it.Signatures[pubHex] = sig
}
// MarshalJSON implements json.Marshaler interface.
func (it Item) MarshalJSON() ([]byte, error) {
ci := itemAux{
Script: it.Script,
Parameters: it.Parameters,
Signatures: make(map[string]string, len(it.Signatures)),
}
for key, sig := range it.Signatures {
ci.Signatures[key] = hex.EncodeToString(sig)
}
return json.Marshal(ci)
}
// UnmarshalJSON implements json.Unmarshaler interface.
func (it *Item) UnmarshalJSON(data []byte) error {
ci := new(itemAux)
if err := json.Unmarshal(data, ci); err != nil {
return err
}
sigs := make(map[string][]byte, len(ci.Signatures))
for keyHex, sigHex := range ci.Signatures {
_, err := keys.NewPublicKeyFromString(keyHex)
if err != nil {
return err
}
sig, err := hex.DecodeString(sigHex)
if err != nil {
return err
}
sigs[keyHex] = sig
}
it.Signatures = sigs
it.Script = ci.Script
it.Parameters = ci.Parameters
return nil
}

View file

@ -0,0 +1,74 @@
package context
import (
"encoding/hex"
"encoding/json"
"io"
"math/rand"
"testing"
"time"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestContextItem_AddSignature(t *testing.T) {
item := &Item{Signatures: make(map[string][]byte)}
priv1, err := keys.NewPrivateKey()
require.NoError(t, err)
pub1 := priv1.PublicKey()
sig1 := []byte{1, 2, 3}
item.AddSignature(pub1, sig1)
require.Equal(t, sig1, item.GetSignature(pub1))
priv2, err := keys.NewPrivateKey()
require.NoError(t, err)
pub2 := priv2.PublicKey()
sig2 := []byte{5, 6, 7}
item.AddSignature(pub2, sig2)
require.Equal(t, sig2, item.GetSignature(pub2))
require.Equal(t, sig1, item.GetSignature(pub1))
}
func TestContextItem_MarshalJSON(t *testing.T) {
priv1, err := keys.NewPrivateKey()
require.NoError(t, err)
priv2, err := keys.NewPrivateKey()
require.NoError(t, err)
expected := &Item{
Script: util.Uint160{1, 2, 3},
Parameters: []smartcontract.Parameter{{
Type: smartcontract.SignatureType,
Value: getRandomSlice(t, 64),
}},
Signatures: map[string][]byte{
hex.EncodeToString(priv1.PublicKey().Bytes()): getRandomSlice(t, 64),
hex.EncodeToString(priv2.PublicKey().Bytes()): getRandomSlice(t, 64),
},
}
data, err := json.Marshal(expected)
require.NoError(t, err)
actual := new(Item)
require.NoError(t, json.Unmarshal(data, actual))
assert.Equal(t, expected, actual)
}
func getRandomSlice(t *testing.T, n int) []byte {
src := rand.NewSource(time.Now().UnixNano())
r := rand.New(src)
data := make([]byte, n)
_, err := io.ReadFull(r, data)
require.NoError(t, err)
return data
}