Skip to content

Commit

Permalink
fix(gnovm): correct map key persistence (#3127)
Browse files Browse the repository at this point in the history
# update: 

closes: #2060 and some other
issues, by:

The main issue is that after the pointer value is retrieved from the
store, the `TV` field is not correctly filled, it leads to inconsistent
map key calculation. this PR solves this problem.


===========================================================
~~closes: #2060 and some other
issues, by:~~

~~- correct map key computation when it's a pointer;~~
~~- make key object owned by map value.~~

~~The root cause of the issue lies in how the map key is calculated when
using a pointer type. Currently, the memory address of the pointer value
is used to generate the map key. However, this approach is inconsistent
between the VM space and storage space. As a result, if a map key
derived from a pointer is persisted and later restored, the two keys do
not match.~~

~~The proposed solution is to derive an immutable identifier for pointer
values. This involves calculating an absolute path for the variables
that the pointer value references, ensuring a corresponding and
consistent `origin`.~~

~~The absolute path consists of `pkgId:blockId:[index]`, providing a
globally unique identifier for the pointer.~~

~~For example: In the code below, the map key `i`, which is a pointer
value, has an origin represented as:
`15bc39c5756bbda67fd0d56bc9e86b29d7b444fc:1:[1]`~~


~~// PKGPATH: gno.land/r/ptr_map~~
~~package ptr_map~~

~~var (
	m = map[*int]int{}
	i = new(int)
)~~

~~func AddToMap(value int) {
	m[i] = value
}~~

~~func GetFromMap() int {
	return m[i]
}~~

~~func init() {
	*i = 1
	AddToMap(5)
}~~

~~// ----above is initialized and persisted before main is executed.~~

~~func main() {
	~~r := GetFromMap()
	println(r == 5)~~
        *i = 2 // this changes TV, also Base of a pointer value
	r = GetFromMap()
	println(r == 5)~~
}~~

~~// Output:~~
~~// true~~
~~// true~~
  • Loading branch information
ltzmaxwell authored Jan 20, 2025
1 parent 09764ad commit 52ddd00
Show file tree
Hide file tree
Showing 28 changed files with 745 additions and 1 deletion.
31 changes: 31 additions & 0 deletions gno.land/pkg/integration/testdata/ptr_mapkey.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
gnoland start

# add contract
gnokey maketx addpkg -pkgdir $WORK -pkgpath gno.land/r/demo/ptrmap -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
stdout OK!

gnokey maketx call -pkgpath gno.land/r/demo/ptrmap -func AddToMap -args 5 -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
stdout OK!

gnokey maketx call -pkgpath gno.land/r/demo/ptrmap -func GetFromMap -gas-fee 1000000ugnot -gas-wanted 2000000 -broadcast -chainid=tendermint_test test1
stdout '(5 int)'
stdout OK!

-- gno.mod --
module gno.land/r/demo/ptrmap

-- realm.gno --
package ptrmap

var (
m = map[*int]int{}
i = new(int)
)

func AddToMap(value int) {
m[i] = value
}

func GetFromMap() int {
return m[i]
}
3 changes: 2 additions & 1 deletion gnovm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ run.bench.storage: build.bench.storage
########################################
# Test suite
.PHONY: test
test: _test.cmd _test.pkg _test.stdlibs
test: _test.filetest _test.cmd _test.pkg _test.stdlibs

.PHONY: _test.cmd
_test.cmd:
Expand Down Expand Up @@ -116,6 +116,7 @@ _test.pkg:
_test.stdlibs:
go run ./cmd/gno test -v ./stdlibs/...

.PHONY: _test.filetest
_test.filetest:;
go test pkg/gnolang/files_test.go -test.short -run 'TestFiles$$/' $(GOTEST_FLAGS)

Expand Down
1 change: 1 addition & 0 deletions gnovm/pkg/gnolang/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -1563,6 +1563,7 @@ func (tv *TypedValue) ComputeMapKey(store Store, omitType bool) MapKey {
pbz := tv.PrimitiveBytes()
bz = append(bz, pbz...)
case *PointerType:
fillValueTV(store, tv)
ptr := uintptr(unsafe.Pointer(tv.V.(PointerValue).TV))
bz = append(bz, uintptrToBytes(&ptr)...)
case FieldType:
Expand Down
27 changes: 27 additions & 0 deletions gnovm/tests/files/ptrmap_1.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// PKGPATH: gno.land/r/ptr_map
package ptr_map

var (
m = map[int]int{}
i = 0
)

func AddToMap(value int) {
m[i] = value
}

func GetFromMap() int {
return m[i]
}

func init() {
AddToMap(5)
}

func main() {
r := GetFromMap()
println(r == 5)
}

// Output:
// true
21 changes: 21 additions & 0 deletions gnovm/tests/files/ptrmap_10.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// PKGPATH: gno.land/r/ptr_map
package ptr_map

var (
m = map[*int]string{}
a, b = 1, 2
arr = [2]*int{&a, &b}
)

func init() {
m[arr[0]] = "first key"
}

func main() {
println(m[arr[0]]) // Output: first key
println(m[arr[1]] == "")
}

// Output:
// first key
// true
22 changes: 22 additions & 0 deletions gnovm/tests/files/ptrmap_11.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// PKGPATH: gno.land/r/ptr_map
package ptr_map

var (
m = map[*int]string{}
a, b = 1, 2
S = []*int{&a, &b} // slice
index = 0
)

func init() {
m[S[index]] = "first key"
}

func main() {
println(m[S[index]]) // Output: first key
println(m[S[1]] == "")
}

// Output:
// first key
// true
21 changes: 21 additions & 0 deletions gnovm/tests/files/ptrmap_12.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// PKGPATH: gno.land/r/ptr_map
package ptr_map

var (
m = map[*int]string{}
a, b = 1, 2
S = []*int{&a, &b}
)

func init() {
m[S[0]] = "first key"
}

func main() {
println(m[S[0]]) // Output: first key
println(m[S[1]] == "")
}

// Output:
// first key
// true
22 changes: 22 additions & 0 deletions gnovm/tests/files/ptrmap_13.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// PKGPATH: gno.land/r/ptr_map
package ptr_map

type MyStruct struct {
Index int
}

var m = make(map[*int]string)
var a, b = 1, 2
var s = []*int{&a, &b}
var myStruct = MyStruct{Index: 0}

func init() {
m[s[myStruct.Index]] = "a"
}

func main() {
println(m[s[myStruct.Index]])
}

// Output:
// a
20 changes: 20 additions & 0 deletions gnovm/tests/files/ptrmap_14.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// PKGPATH: gno.land/r/ptr_map
package ptr_map

var (
m = map[*int]string{}
a = 0
ptr *int = &a // A pointer to an int
i1 **int = &ptr
)

func init() {
m[*i1] = "first key"
}

func main() {
println(m[*i1]) // Output: first key
}

// Output:
// first key
23 changes: 23 additions & 0 deletions gnovm/tests/files/ptrmap_15.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// PKGPATH: gno.land/r/ptr_map
package ptr_map

type MyStruct struct {
Key *int
}

var (
m = map[*int]string{}
i1 = MyStruct{Key: new(int)}
)

func init() {
*i1.Key = 1 // Set the value of the pointer
m[i1.Key] = "first key"
}

func main() {
println(m[i1.Key]) // Output: first key
}

// Output:
// first key
24 changes: 24 additions & 0 deletions gnovm/tests/files/ptrmap_16.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// PKGPATH: gno.land/r/ptr_map
package ptr_map

type Foo struct {
name string
}

var (
arr = [3]Foo{Foo{"a"}, Foo{"b"}, Foo{"c"}}
m = map[*Foo]string{}
)

func init() {
m[&arr[0]] = "first key"
}

func main() {
println(m[&arr[0]])
println(m[&arr[1]] == "")
}

// Output:
// first key
// true
23 changes: 23 additions & 0 deletions gnovm/tests/files/ptrmap_17.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// PKGPATH: gno.land/r/ptr_map
package ptr_map

type Foo struct {
name string
}

var (
arr = [3]Foo{Foo{"a"}, Foo{"b"}, Foo{"c"}}
m = map[*Foo]string{}
index = 0
)

func init() {
m[&arr[index]] = "first key"
}

func main() {
println(m[&arr[index]])
}

// Output:
// first key
33 changes: 33 additions & 0 deletions gnovm/tests/files/ptrmap_18.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// PKGPATH: gno.land/r/ptr_map
package ptr_map

var (
m = map[*int]int{}
i = 0
)

func AddToMap(value int) {
m[&i] = value
}

func GetFromMap() int {
return m[&i]
}

func init() {
i = 1
AddToMap(5)
}

func main() {
r := GetFromMap()
println(r == 5)

i = 2
r = GetFromMap()
println(r == 5)
}

// Output:
// true
// true
37 changes: 37 additions & 0 deletions gnovm/tests/files/ptrmap_19.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// PKGPATH: gno.land/r/ptr_map
package ptr_map

var (
m = map[*int]int{}
i = 0
)

func AddToMap(value int) {
m[&i] = value
}

func GetFromMap() int {
i := 0
{
{
return m[&i]
}
}
}

func init() {
AddToMap(5)
}

func main() {
r := GetFromMap()
println(r == 5)

i = 2
r = GetFromMap()
println(r == 5)
}

// Output:
// false
// false
35 changes: 35 additions & 0 deletions gnovm/tests/files/ptrmap_2.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// PKGPATH: gno.land/r/ptr_map
package ptr_map

var (
m = map[*int]int{}
i = new(int)
)

func AddToMap(value int) {
m[i] = value
}

func GetFromMap() int {
return m[i]
}

func init() {
*i = 1
AddToMap(5)
}

// ----above is initialized and persisted before main is executed.

func main() {
r := GetFromMap()
println(r == 5)

*i = 2 // this changes TV, also Base of a pointer value
r = GetFromMap()
println(r == 5)
}

// Output:
// true
// true
Loading

0 comments on commit 52ddd00

Please sign in to comment.