commit
20fcbda91c
11 changed files with 169 additions and 198 deletions
|
@ -450,10 +450,11 @@ func (ic *interopContext) storageFind(v *vm.VM) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredMap := make(map[interface{}]vm.StackItem)
|
filteredMap := vm.NewMapItem()
|
||||||
for k, v := range siMap {
|
for k, v := range siMap {
|
||||||
if strings.HasPrefix(k, prefix) {
|
if strings.HasPrefix(k, prefix) {
|
||||||
filteredMap[k] = vm.NewByteArrayItem(v.Value)
|
filteredMap.Add(vm.NewByteArrayItem([]byte(k)),
|
||||||
|
vm.NewByteArrayItem(v.Value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,13 @@ type Parameter struct {
|
||||||
Value interface{} `json:"value"`
|
Value interface{} `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParameterPair represents key-value pair, a slice of which is stored in
|
||||||
|
// MapType Parameter.
|
||||||
|
type ParameterPair struct {
|
||||||
|
Key Parameter `json:"key"`
|
||||||
|
Value Parameter `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
// NewParameter returns a Parameter with proper initialized Value
|
// NewParameter returns a Parameter with proper initialized Value
|
||||||
// of the given ParamType.
|
// of the given ParamType.
|
||||||
func NewParameter(t ParamType) Parameter {
|
func NewParameter(t ParamType) Parameter {
|
||||||
|
@ -49,16 +56,6 @@ type rawParameter struct {
|
||||||
Value json.RawMessage `json:"value"`
|
Value json.RawMessage `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type keyValuePair struct {
|
|
||||||
Key rawParameter `json:"key"`
|
|
||||||
Value rawParameter `json:"value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type rawKeyValuePair struct {
|
|
||||||
Key json.RawMessage `json:"key"`
|
|
||||||
Value json.RawMessage `json:"value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON implements Marshaler interface.
|
// MarshalJSON implements Marshaler interface.
|
||||||
func (p *Parameter) MarshalJSON() ([]byte, error) {
|
func (p *Parameter) MarshalJSON() ([]byte, error) {
|
||||||
var (
|
var (
|
||||||
|
@ -85,38 +82,11 @@ func (p *Parameter) MarshalJSON() ([]byte, error) {
|
||||||
resultRawValue, resultErr = json.Marshal(hex.EncodeToString(p.Value.([]byte)))
|
resultRawValue, resultErr = json.Marshal(hex.EncodeToString(p.Value.([]byte)))
|
||||||
}
|
}
|
||||||
case ArrayType:
|
case ArrayType:
|
||||||
var value = make([]json.RawMessage, 0)
|
var value = p.Value.([]Parameter)
|
||||||
for _, parameter := range p.Value.([]Parameter) {
|
|
||||||
rawValue, err := json.Marshal(¶meter)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
value = append(value, rawValue)
|
|
||||||
}
|
|
||||||
resultRawValue, resultErr = json.Marshal(value)
|
resultRawValue, resultErr = json.Marshal(value)
|
||||||
case MapType:
|
case MapType:
|
||||||
var value []keyValuePair
|
ppair := p.Value.([]ParameterPair)
|
||||||
for key, val := range p.Value.(map[Parameter]Parameter) {
|
resultRawValue, resultErr = json.Marshal(ppair)
|
||||||
rawKey, err := json.Marshal(key.Value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rawValue, err := json.Marshal(val.Value)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
value = append(value, keyValuePair{
|
|
||||||
Key: rawParameter{
|
|
||||||
Type: key.Type,
|
|
||||||
Value: rawKey,
|
|
||||||
},
|
|
||||||
Value: rawParameter{
|
|
||||||
Type: val.Type,
|
|
||||||
Value: rawValue,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
resultRawValue, resultErr = json.Marshal(value)
|
|
||||||
default:
|
default:
|
||||||
resultErr = errors.Errorf("Marshaller for type %s not implemented", p.Type)
|
resultErr = errors.Errorf("Marshaller for type %s not implemented", p.Type)
|
||||||
}
|
}
|
||||||
|
@ -181,22 +151,11 @@ func (p *Parameter) UnmarshalJSON(data []byte) (err error) {
|
||||||
}
|
}
|
||||||
p.Value = rs
|
p.Value = rs
|
||||||
case MapType:
|
case MapType:
|
||||||
var rawMap []rawKeyValuePair
|
var ppair []ParameterPair
|
||||||
if err = json.Unmarshal(r.Value, &rawMap); err != nil {
|
if err = json.Unmarshal(r.Value, &ppair); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rs := make(map[Parameter]Parameter)
|
p.Value = ppair
|
||||||
for _, p := range rawMap {
|
|
||||||
var key, value Parameter
|
|
||||||
if err = json.Unmarshal(p.Key, &key); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = json.Unmarshal(p.Value, &value); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rs[key] = value
|
|
||||||
}
|
|
||||||
p.Value = rs
|
|
||||||
case Hash160Type:
|
case Hash160Type:
|
||||||
var h util.Uint160
|
var h util.Uint160
|
||||||
if err = json.Unmarshal(r.Value, &h); err != nil {
|
if err = json.Unmarshal(r.Value, &h); err != nil {
|
||||||
|
@ -234,13 +193,7 @@ func (p *Parameter) EncodeBinary(w *io.BinWriter) {
|
||||||
case ArrayType:
|
case ArrayType:
|
||||||
w.WriteArray(p.Value.([]Parameter))
|
w.WriteArray(p.Value.([]Parameter))
|
||||||
case MapType:
|
case MapType:
|
||||||
m := p.Value.(map[Parameter]Parameter)
|
w.WriteArray(p.Value.([]ParameterPair))
|
||||||
w.WriteVarUint(uint64(len(m)))
|
|
||||||
for k := range m {
|
|
||||||
v := m[k]
|
|
||||||
k.EncodeBinary(w)
|
|
||||||
v.EncodeBinary(w)
|
|
||||||
}
|
|
||||||
case Hash160Type:
|
case Hash160Type:
|
||||||
w.WriteBytes(p.Value.(util.Uint160).BytesBE())
|
w.WriteBytes(p.Value.(util.Uint160).BytesBE())
|
||||||
case Hash256Type:
|
case Hash256Type:
|
||||||
|
@ -273,15 +226,9 @@ func (p *Parameter) DecodeBinary(r *io.BinReader) {
|
||||||
r.ReadArray(&ps)
|
r.ReadArray(&ps)
|
||||||
p.Value = ps
|
p.Value = ps
|
||||||
case MapType:
|
case MapType:
|
||||||
ln := r.ReadVarUint()
|
ps := []ParameterPair{}
|
||||||
m := make(map[Parameter]Parameter, ln)
|
r.ReadArray(&ps)
|
||||||
for i := uint64(0); i < ln; i++ {
|
p.Value = ps
|
||||||
var k, v Parameter
|
|
||||||
k.DecodeBinary(r)
|
|
||||||
v.DecodeBinary(r)
|
|
||||||
m[k] = v
|
|
||||||
}
|
|
||||||
p.Value = m
|
|
||||||
case Hash160Type:
|
case Hash160Type:
|
||||||
var u util.Uint160
|
var u util.Uint160
|
||||||
r.ReadBytes(u[:])
|
r.ReadBytes(u[:])
|
||||||
|
@ -296,6 +243,18 @@ func (p *Parameter) DecodeBinary(r *io.BinReader) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements io.Serializable interface.
|
||||||
|
func (p *ParameterPair) EncodeBinary(w *io.BinWriter) {
|
||||||
|
p.Key.EncodeBinary(w)
|
||||||
|
p.Value.EncodeBinary(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinary implements io.Serializable interface.
|
||||||
|
func (p *ParameterPair) DecodeBinary(r *io.BinReader) {
|
||||||
|
p.Key.DecodeBinary(r)
|
||||||
|
p.Value.DecodeBinary(r)
|
||||||
|
}
|
||||||
|
|
||||||
// Params is an array of Parameter (TODO: drop it?).
|
// Params is an array of Parameter (TODO: drop it?).
|
||||||
type Params []Parameter
|
type Params []Parameter
|
||||||
|
|
||||||
|
|
|
@ -72,9 +72,15 @@ var marshalJSONTestCases = []struct {
|
||||||
{
|
{
|
||||||
input: Parameter{
|
input: Parameter{
|
||||||
Type: MapType,
|
Type: MapType,
|
||||||
Value: map[Parameter]Parameter{
|
Value: []ParameterPair{
|
||||||
{Type: StringType, Value: "key1"}: {Type: IntegerType, Value: int64(1)},
|
{
|
||||||
{Type: StringType, Value: "key2"}: {Type: StringType, Value: "two"},
|
Key: Parameter{Type: StringType, Value: "key1"},
|
||||||
|
Value: Parameter{Type: IntegerType, Value: int64(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: Parameter{Type: StringType, Value: "key2"},
|
||||||
|
Value: Parameter{Type: StringType, Value: "two"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: `{"type":"Map","value":[{"key":{"type":"String","value":"key1"},"value":{"type":"Integer","value":1}},{"key":{"type":"String","value":"key2"},"value":{"type":"String","value":"two"}}]}`,
|
result: `{"type":"Map","value":[{"key":{"type":"String","value":"key1"},"value":{"type":"Integer","value":1}},{"key":{"type":"String","value":"key2"},"value":{"type":"String","value":"two"}}]}`,
|
||||||
|
@ -82,11 +88,14 @@ var marshalJSONTestCases = []struct {
|
||||||
{
|
{
|
||||||
input: Parameter{
|
input: Parameter{
|
||||||
Type: MapType,
|
Type: MapType,
|
||||||
Value: map[Parameter]Parameter{
|
Value: []ParameterPair{
|
||||||
{Type: StringType, Value: "key1"}: {Type: ArrayType, Value: []Parameter{
|
{
|
||||||
{Type: StringType, Value: "str 1"},
|
Key: Parameter{Type: StringType, Value: "key1"},
|
||||||
{Type: IntegerType, Value: int64(2)},
|
Value: Parameter{Type: ArrayType, Value: []Parameter{
|
||||||
}},
|
{Type: StringType, Value: "str 1"},
|
||||||
|
{Type: IntegerType, Value: int64(2)},
|
||||||
|
}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: `{"type":"Map","value":[{"key":{"type":"String","value":"key1"},"value":{"type":"Array","value":[{"type":"String","value":"str 1"},{"type":"Integer","value":2}]}}]}`,
|
result: `{"type":"Map","value":[{"key":{"type":"String","value":"key1"},"value":{"type":"Array","value":[{"type":"String","value":"str 1"},{"type":"Integer","value":2}]}}]}`,
|
||||||
|
@ -209,9 +218,15 @@ var unmarshalJSONTestCases = []struct {
|
||||||
input: `{"type":"Map","value":[{"key":{"type":"String","value":"key1"},"value":{"type":"Integer","value":1}},{"key":{"type":"String","value":"key2"},"value":{"type":"String","value":"two"}}]}`,
|
input: `{"type":"Map","value":[{"key":{"type":"String","value":"key1"},"value":{"type":"Integer","value":1}},{"key":{"type":"String","value":"key2"},"value":{"type":"String","value":"two"}}]}`,
|
||||||
result: Parameter{
|
result: Parameter{
|
||||||
Type: MapType,
|
Type: MapType,
|
||||||
Value: map[Parameter]Parameter{
|
Value: []ParameterPair{
|
||||||
{Type: StringType, Value: "key1"}: {Type: IntegerType, Value: int64(1)},
|
{
|
||||||
{Type: StringType, Value: "key2"}: {Type: StringType, Value: "two"},
|
Key: Parameter{Type: StringType, Value: "key1"},
|
||||||
|
Value: Parameter{Type: IntegerType, Value: int64(1)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: Parameter{Type: StringType, Value: "key2"},
|
||||||
|
Value: Parameter{Type: StringType, Value: "two"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -219,11 +234,14 @@ var unmarshalJSONTestCases = []struct {
|
||||||
input: `{"type":"Map","value":[{"key":{"type":"String","value":"key1"},"value":{"type":"Array","value":[{"type":"String","value":"str 1"},{"type":"Integer","value":2}]}}]}`,
|
input: `{"type":"Map","value":[{"key":{"type":"String","value":"key1"},"value":{"type":"Array","value":[{"type":"String","value":"str 1"},{"type":"Integer","value":2}]}}]}`,
|
||||||
result: Parameter{
|
result: Parameter{
|
||||||
Type: MapType,
|
Type: MapType,
|
||||||
Value: map[Parameter]Parameter{
|
Value: []ParameterPair{
|
||||||
{Type: StringType, Value: "key1"}: {Type: ArrayType, Value: []Parameter{
|
{
|
||||||
{Type: StringType, Value: "str 1"},
|
Key: Parameter{Type: StringType, Value: "key1"},
|
||||||
{Type: IntegerType, Value: int64(2)},
|
Value: Parameter{Type: ArrayType, Value: []Parameter{
|
||||||
}},
|
{Type: StringType, Value: "str 1"},
|
||||||
|
{Type: IntegerType, Value: int64(2)},
|
||||||
|
}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -185,7 +185,7 @@ func IteratorCreate(v *VM) error {
|
||||||
value: t.Value().([]StackItem),
|
value: t.Value().([]StackItem),
|
||||||
})
|
})
|
||||||
case *MapItem:
|
case *MapItem:
|
||||||
item = NewMapIterator(t.value)
|
item = NewMapIterator(t)
|
||||||
default:
|
default:
|
||||||
return errors.New("non-iterable type")
|
return errors.New("non-iterable type")
|
||||||
}
|
}
|
||||||
|
@ -195,16 +195,10 @@ func IteratorCreate(v *VM) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMapIterator returns new interop item containing iterator over m.
|
// NewMapIterator returns new interop item containing iterator over m.
|
||||||
func NewMapIterator(m map[interface{}]StackItem) *InteropItem {
|
func NewMapIterator(m *MapItem) *InteropItem {
|
||||||
keys := make([]interface{}, 0, len(m))
|
|
||||||
for k := range m {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewInteropItem(&mapWrapper{
|
return NewInteropItem(&mapWrapper{
|
||||||
index: -1,
|
index: -1,
|
||||||
keys: keys,
|
m: m.value,
|
||||||
m: m,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,7 @@ type (
|
||||||
|
|
||||||
mapWrapper struct {
|
mapWrapper struct {
|
||||||
index int
|
index int
|
||||||
keys []interface{}
|
m []MapElement
|
||||||
m map[interface{}]StackItem
|
|
||||||
}
|
}
|
||||||
|
|
||||||
concatIter struct {
|
concatIter struct {
|
||||||
|
@ -91,7 +90,7 @@ func (i *concatIter) Key() StackItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mapWrapper) Next() bool {
|
func (m *mapWrapper) Next() bool {
|
||||||
if next := m.index + 1; next < len(m.keys) {
|
if next := m.index + 1; next < len(m.m) {
|
||||||
m.index = next
|
m.index = next
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -100,11 +99,11 @@ func (m *mapWrapper) Next() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mapWrapper) Value() StackItem {
|
func (m *mapWrapper) Value() StackItem {
|
||||||
return m.m[m.keys[m.index]]
|
return m.m[m.index].Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mapWrapper) Key() StackItem {
|
func (m *mapWrapper) Key() StackItem {
|
||||||
return makeStackItem(m.keys[m.index])
|
return m.m[m.index].Key
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *keysWrapper) Next() bool {
|
func (e *keysWrapper) Next() bool {
|
||||||
|
|
|
@ -73,9 +73,9 @@ func serializeItemTo(item StackItem, w *io.BinWriter, seen map[StackItem]bool) {
|
||||||
|
|
||||||
w.WriteBytes([]byte{byte(mapT)})
|
w.WriteBytes([]byte{byte(mapT)})
|
||||||
w.WriteVarUint(uint64(len(t.value)))
|
w.WriteVarUint(uint64(len(t.value)))
|
||||||
for k, v := range t.value {
|
for i := range t.value {
|
||||||
serializeItemTo(makeStackItem(k), w, seen)
|
serializeItemTo(t.value[i].Key, w, seen)
|
||||||
serializeItemTo(v, w, seen)
|
serializeItemTo(t.value[i].Value, w, seen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,8 +228,8 @@ func (s *Stack) updateSizeAdd(item StackItem) {
|
||||||
s.updateSizeAdd(it)
|
s.updateSizeAdd(it)
|
||||||
}
|
}
|
||||||
case *MapItem:
|
case *MapItem:
|
||||||
for _, v := range t.value {
|
for i := range t.value {
|
||||||
s.updateSizeAdd(v)
|
s.updateSizeAdd(t.value[i].Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,8 +253,8 @@ func (s *Stack) updateSizeRemove(item StackItem) {
|
||||||
s.updateSizeRemove(it)
|
s.updateSizeRemove(it)
|
||||||
}
|
}
|
||||||
case *MapItem:
|
case *MapItem:
|
||||||
for _, v := range t.value {
|
for i := range t.value {
|
||||||
s.updateSizeRemove(v)
|
s.updateSizeRemove(t.value[i].Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -435,15 +435,25 @@ func (i *ArrayItem) ToContractParameter(seen map[StackItem]bool) smartcontract.P
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapItem represents Map object.
|
// MapElement is a key-value pair of StackItems.
|
||||||
|
type MapElement struct {
|
||||||
|
Key StackItem
|
||||||
|
Value StackItem
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapItem represents Map object. It's ordered, so we use slice representation
|
||||||
|
// which should be fine for maps with less than 32 or so elements. Given that
|
||||||
|
// our VM has quite low limit of overall stack items, it should be good enough,
|
||||||
|
// but it can be extended with a real map for fast random access in the future
|
||||||
|
// if need be.
|
||||||
type MapItem struct {
|
type MapItem struct {
|
||||||
value map[interface{}]StackItem
|
value []MapElement
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMapItem returns new MapItem object.
|
// NewMapItem returns new MapItem object.
|
||||||
func NewMapItem() *MapItem {
|
func NewMapItem() *MapItem {
|
||||||
return &MapItem{
|
return &MapItem{
|
||||||
value: make(map[interface{}]StackItem),
|
value: make([]MapElement, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,10 +476,19 @@ func (i *MapItem) String() string {
|
||||||
return "Map"
|
return "Map"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Index returns an index of the key in map.
|
||||||
|
func (i *MapItem) Index(key StackItem) int {
|
||||||
|
for k := range i.value {
|
||||||
|
if i.value[k].Key.Equals(key) {
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
// Has checks if map has specified key.
|
// Has checks if map has specified key.
|
||||||
func (i *MapItem) Has(key StackItem) (ok bool) {
|
func (i *MapItem) Has(key StackItem) bool {
|
||||||
_, ok = i.value[toMapKey(key)]
|
return i.Index(key) >= 0
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dup implements StackItem interface.
|
// Dup implements StackItem interface.
|
||||||
|
@ -480,16 +499,14 @@ func (i *MapItem) Dup() StackItem {
|
||||||
|
|
||||||
// ToContractParameter implements StackItem interface.
|
// ToContractParameter implements StackItem interface.
|
||||||
func (i *MapItem) ToContractParameter(seen map[StackItem]bool) smartcontract.Parameter {
|
func (i *MapItem) ToContractParameter(seen map[StackItem]bool) smartcontract.Parameter {
|
||||||
value := make(map[smartcontract.Parameter]smartcontract.Parameter)
|
value := make([]smartcontract.ParameterPair, 0)
|
||||||
if !seen[i] {
|
if !seen[i] {
|
||||||
seen[i] = true
|
seen[i] = true
|
||||||
for key, val := range i.value {
|
for k := range i.value {
|
||||||
pValue := val.ToContractParameter(seen)
|
value = append(value, smartcontract.ParameterPair{
|
||||||
pKey := fromMapKey(key).ToContractParameter(seen)
|
Key: i.value[k].Key.ToContractParameter(seen),
|
||||||
if pKey.Type == smartcontract.ByteArrayType {
|
Value: i.value[k].Value.ToContractParameter(seen),
|
||||||
pKey.Value = string(pKey.Value.([]byte))
|
})
|
||||||
}
|
|
||||||
value[pKey] = pValue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return smartcontract.Parameter{
|
return smartcontract.Parameter{
|
||||||
|
@ -500,34 +517,31 @@ func (i *MapItem) ToContractParameter(seen map[StackItem]bool) smartcontract.Par
|
||||||
|
|
||||||
// Add adds key-value pair to the map.
|
// Add adds key-value pair to the map.
|
||||||
func (i *MapItem) Add(key, value StackItem) {
|
func (i *MapItem) Add(key, value StackItem) {
|
||||||
i.value[toMapKey(key)] = value
|
if !isValidMapKey(key) {
|
||||||
}
|
|
||||||
|
|
||||||
// toMapKey converts StackItem so that it can be used as a map key.
|
|
||||||
func toMapKey(key StackItem) interface{} {
|
|
||||||
switch t := key.(type) {
|
|
||||||
case *BoolItem:
|
|
||||||
return t.value
|
|
||||||
case *BigIntegerItem:
|
|
||||||
return t.value.Int64()
|
|
||||||
case *ByteArrayItem:
|
|
||||||
return string(t.value)
|
|
||||||
default:
|
|
||||||
panic("wrong key type")
|
panic("wrong key type")
|
||||||
}
|
}
|
||||||
|
index := i.Index(key)
|
||||||
|
if index >= 0 {
|
||||||
|
i.value[index].Value = value
|
||||||
|
} else {
|
||||||
|
i.value = append(i.value, MapElement{key, value})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fromMapKey converts map key to StackItem
|
// Drop removes given index from the map (no bounds check done here).
|
||||||
func fromMapKey(key interface{}) StackItem {
|
func (i *MapItem) Drop(index int) {
|
||||||
switch t := key.(type) {
|
copy(i.value[index:], i.value[index+1:])
|
||||||
case bool:
|
i.value = i.value[:len(i.value)-1]
|
||||||
return &BoolItem{value: t}
|
}
|
||||||
case int64:
|
|
||||||
return &BigIntegerItem{value: big.NewInt(t)}
|
// isValidMapKey checks whether it's possible to use given StackItem as a Map
|
||||||
case string:
|
// key.
|
||||||
return &ByteArrayItem{value: []byte(t)}
|
func isValidMapKey(key StackItem) bool {
|
||||||
|
switch key.(type) {
|
||||||
|
case *BoolItem, *BigIntegerItem, *ByteArrayItem:
|
||||||
|
return true
|
||||||
default:
|
default:
|
||||||
panic("wrong key type")
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -282,13 +282,13 @@ var equalsTestCases = map[string][]struct {
|
||||||
result: false,
|
result: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
item1: &MapItem{value: map[interface{}]StackItem{"first": NewBigIntegerItem(1), true: NewByteArrayItem([]byte{2})}},
|
item1: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(1)}, {NewBoolItem(true), NewByteArrayItem([]byte{2})}}},
|
||||||
item2: &MapItem{value: map[interface{}]StackItem{"first": NewBigIntegerItem(1), true: NewByteArrayItem([]byte{2})}},
|
item2: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(1)}, {NewBoolItem(true), NewByteArrayItem([]byte{2})}}},
|
||||||
result: false,
|
result: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
item1: &MapItem{value: map[interface{}]StackItem{"first": NewBigIntegerItem(1), true: NewByteArrayItem([]byte{2})}},
|
item1: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(1)}, {NewBoolItem(true), NewByteArrayItem([]byte{2})}}},
|
||||||
item2: &MapItem{value: map[interface{}]StackItem{"first": NewBigIntegerItem(1), true: NewByteArrayItem([]byte{3})}},
|
item2: &MapItem{value: []MapElement{{NewByteArrayItem([]byte("first")), NewBigIntegerItem(1)}, {NewBoolItem(true), NewByteArrayItem([]byte{3})}}},
|
||||||
result: false,
|
result: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -414,17 +414,25 @@ var toContractParameterTestCases = []struct {
|
||||||
result: smartcontract.Parameter{Type: smartcontract.InteropInterfaceType, Value: nil},
|
result: smartcontract.Parameter{Type: smartcontract.InteropInterfaceType, Value: nil},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: &MapItem{value: map[interface{}]StackItem{
|
input: &MapItem{value: []MapElement{
|
||||||
toMapKey(NewBigIntegerItem(1)): NewBoolItem(true),
|
{NewBigIntegerItem(1), NewBoolItem(true)},
|
||||||
toMapKey(NewByteArrayItem([]byte("qwerty"))): NewBigIntegerItem(3),
|
{NewByteArrayItem([]byte("qwerty")), NewBigIntegerItem(3)},
|
||||||
toMapKey(NewBoolItem(true)): NewBoolItem(false),
|
{NewBoolItem(true), NewBoolItem(false)},
|
||||||
}},
|
}},
|
||||||
result: smartcontract.Parameter{
|
result: smartcontract.Parameter{
|
||||||
Type: smartcontract.MapType,
|
Type: smartcontract.MapType,
|
||||||
Value: map[smartcontract.Parameter]smartcontract.Parameter{
|
Value: []smartcontract.ParameterPair{
|
||||||
{Type: smartcontract.IntegerType, Value: int64(1)}: {Type: smartcontract.BoolType, Value: true},
|
{
|
||||||
{Type: smartcontract.ByteArrayType, Value: "qwerty"}: {Type: smartcontract.IntegerType, Value: int64(3)},
|
Key: smartcontract.Parameter{Type: smartcontract.IntegerType, Value: int64(1)},
|
||||||
{Type: smartcontract.BoolType, Value: true}: {Type: smartcontract.BoolType, Value: false},
|
Value: smartcontract.Parameter{Type: smartcontract.BoolType, Value: true},
|
||||||
|
}, {
|
||||||
|
Key: smartcontract.Parameter{Type: smartcontract.ByteArrayType, Value: []byte("qwerty")},
|
||||||
|
Value: smartcontract.Parameter{Type: smartcontract.IntegerType, Value: int64(3)},
|
||||||
|
}, {
|
||||||
|
|
||||||
|
Key: smartcontract.Parameter{Type: smartcontract.BoolType, Value: true},
|
||||||
|
Value: smartcontract.Parameter{Type: smartcontract.BoolType, Value: false},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -437,28 +445,3 @@ func TestToContractParameter(t *testing.T) {
|
||||||
assert.Equal(t, res, tc.result)
|
assert.Equal(t, res, tc.result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fromMapKeyTestCases = []struct {
|
|
||||||
input interface{}
|
|
||||||
result StackItem
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
input: true,
|
|
||||||
result: NewBoolItem(true),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: int64(4),
|
|
||||||
result: NewBigIntegerItem(4),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "qwerty",
|
|
||||||
result: NewByteArrayItem([]byte("qwerty")),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromMapKey(t *testing.T) {
|
|
||||||
for _, tc := range fromMapKeyTestCases {
|
|
||||||
res := fromMapKey(tc.input)
|
|
||||||
assert.Equal(t, res, tc.result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
27
pkg/vm/vm.go
27
pkg/vm/vm.go
|
@ -1014,11 +1014,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
item := arr[index].Dup()
|
item := arr[index].Dup()
|
||||||
v.estack.PushVal(item)
|
v.estack.PushVal(item)
|
||||||
case *MapItem:
|
case *MapItem:
|
||||||
if !t.Has(key.value) {
|
index := t.Index(key.Item())
|
||||||
|
if index < 0 {
|
||||||
panic("invalid key")
|
panic("invalid key")
|
||||||
}
|
}
|
||||||
k := toMapKey(key.value)
|
v.estack.Push(&Element{value: t.value[index].Value.Dup()})
|
||||||
v.estack.Push(&Element{value: t.value[k].Dup()})
|
|
||||||
default:
|
default:
|
||||||
arr := obj.Bytes()
|
arr := obj.Bytes()
|
||||||
if index < 0 || index >= len(arr) {
|
if index < 0 || index >= len(arr) {
|
||||||
|
@ -1091,10 +1091,12 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
a = append(a[:k], a[k+1:]...)
|
a = append(a[:k], a[k+1:]...)
|
||||||
t.value = a
|
t.value = a
|
||||||
case *MapItem:
|
case *MapItem:
|
||||||
m := t.value
|
index := t.Index(key.Item())
|
||||||
k := toMapKey(key.value)
|
// NEO 2.0 doesn't error on missing key.
|
||||||
v.estack.updateSizeRemove(m[k])
|
if index >= 0 {
|
||||||
delete(m, k)
|
v.estack.updateSizeRemove(t.value[index].Value)
|
||||||
|
t.Drop(index)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
panic("REMOVE: invalid type")
|
panic("REMOVE: invalid type")
|
||||||
}
|
}
|
||||||
|
@ -1106,7 +1108,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
switch t := elem.Value().(type) {
|
switch t := elem.Value().(type) {
|
||||||
case []StackItem:
|
case []StackItem:
|
||||||
v.estack.PushVal(len(t))
|
v.estack.PushVal(len(t))
|
||||||
case map[interface{}]StackItem:
|
case []MapElement:
|
||||||
v.estack.PushVal(len(t))
|
v.estack.PushVal(len(t))
|
||||||
default:
|
default:
|
||||||
v.estack.PushVal(len(elem.Bytes()))
|
v.estack.PushVal(len(elem.Bytes()))
|
||||||
|
@ -1253,7 +1255,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
|
|
||||||
arr := make([]StackItem, 0, len(m.value))
|
arr := make([]StackItem, 0, len(m.value))
|
||||||
for k := range m.value {
|
for k := range m.value {
|
||||||
arr = append(arr, makeStackItem(k))
|
arr = append(arr, m.value[k].Key.Dup())
|
||||||
}
|
}
|
||||||
v.estack.PushVal(arr)
|
v.estack.PushVal(arr)
|
||||||
|
|
||||||
|
@ -1274,7 +1276,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
case *MapItem:
|
case *MapItem:
|
||||||
arr = make([]StackItem, 0, len(t.value))
|
arr = make([]StackItem, 0, len(t.value))
|
||||||
for k := range t.value {
|
for k := range t.value {
|
||||||
arr = append(arr, cloneIfStruct(t.value[k]))
|
arr = append(arr, cloneIfStruct(t.value[k].Value))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("not a Map, Array or Struct")
|
panic("not a Map, Array or Struct")
|
||||||
|
@ -1298,7 +1300,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
}
|
}
|
||||||
v.estack.PushVal(index < int64(len(c.Array())))
|
v.estack.PushVal(index < int64(len(c.Array())))
|
||||||
case *MapItem:
|
case *MapItem:
|
||||||
v.estack.PushVal(t.Has(key.value))
|
v.estack.PushVal(t.Has(key.Item()))
|
||||||
default:
|
default:
|
||||||
panic("wrong collection type")
|
panic("wrong collection type")
|
||||||
}
|
}
|
||||||
|
@ -1552,8 +1554,7 @@ func validateMapKey(key *Element) {
|
||||||
if key == nil {
|
if key == nil {
|
||||||
panic("no key found")
|
panic("no key found")
|
||||||
}
|
}
|
||||||
switch key.value.(type) {
|
if !isValidMapKey(key.Item()) {
|
||||||
case *ArrayItem, *StructItem, *MapItem:
|
|
||||||
panic("key can't be a collection")
|
panic("key can't be a collection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1371,8 +1371,10 @@ func TestPICKITEMDupMap(t *testing.T) {
|
||||||
runVM(t, vm)
|
runVM(t, vm)
|
||||||
assert.Equal(t, 2, vm.estack.Len())
|
assert.Equal(t, 2, vm.estack.Len())
|
||||||
assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64())
|
assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64())
|
||||||
items := vm.estack.Pop().Value().(map[interface{}]StackItem)
|
items := vm.estack.Pop().Value().([]MapElement)
|
||||||
assert.Equal(t, big.NewInt(-1), items[string([]byte{42})].Value())
|
assert.Equal(t, 1, len(items))
|
||||||
|
assert.Equal(t, []byte{42}, items[0].Key.Value())
|
||||||
|
assert.Equal(t, big.NewInt(-1), items[0].Value.Value())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPICKITEMMap(t *testing.T) {
|
func TestPICKITEMMap(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue