Merge pull request #2583 from nspcc-dev/getblock-sr

Stateroot-enabled GetBlock
This commit is contained in:
Roman Khimov 2022-07-08 10:00:16 +03:00 committed by GitHub
commit 34ddc99a08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 121 additions and 12 deletions

View file

@ -25,6 +25,9 @@ a dialect of Go rather than a complete port of the language:
in variables and returning the result.
* lambdas are supported, but closures are not.
* maps are supported, but valid map keys are booleans, integers and strings with length <= 64
* converting value to interface type doesn't change the underlying type,
original value will always be used, therefore it never panics and always "succeeds";
it's up to the programmer whether it's a correct use of a value
## VM API (interop layer)
Compiler translates interop function calls into NEO VM syscalls or (for custom

View file

@ -371,7 +371,8 @@ func canConvert(s string) bool {
s = s[len(interopPrefix):]
return s != "/iterator.Iterator" && s != "/storage.Context" &&
s != "/native/ledger.Block" && s != "/native/ledger.Transaction" &&
s != "/native/management.Contract" && s != "/native/neo.AccountState"
s != "/native/management.Contract" && s != "/native/neo.AccountState" &&
s != "/native/ledger.BlockSR"
}
return true
}

View file

@ -914,15 +914,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
return nil
}
case *ast.SelectorExpr:
// If this is a method call we need to walk the AST to load the struct locally.
// Otherwise, this is a function call from an imported package and we can call it
// directly.
name, isMethod := c.getFuncNameFromSelector(fun)
if isMethod {
ast.Walk(c, fun.X)
// Don't forget to add 1 extra argument when its a method.
numArgs++
}
f, ok = c.funcs[name]
if ok {
@ -938,12 +930,25 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
c.emitExplicitConvert(c.typeOf(n.Args[0]), typ)
return nil
}
if isMethod {
// If this is a method call we need to walk the AST to load the struct locally.
// Otherwise, this is a function call from an imported package and we can call it
// directly.
ast.Walk(c, fun.X)
// Don't forget to add 1 extra argument when it's a method.
numArgs++
}
case *ast.ArrayType:
// For now we will assume that there are only byte slice conversions.
// E.g. []byte("foobar") or []byte(scriptHash).
ast.Walk(c, n.Args[0])
c.emitConvert(stackitem.BufferT)
return nil
case *ast.InterfaceType:
// It's a type conversion into some interface. Programmer is responsible
// for the conversion to be appropriate, just load the arg.
ast.Walk(c, n.Args[0])
return nil
case *ast.FuncLit:
isLiteral = true
}
@ -2067,7 +2072,11 @@ func (c *codegen) getFuncNameFromSelector(e *ast.SelectorExpr) (string, bool) {
ident := e.X.(*ast.Ident)
if c.typeInfo.Selections[e] != nil {
typ := c.typeInfo.Types[ident].Type.String()
return c.getIdentName(typ, e.Sel.Name), true
name := c.getIdentName(typ, e.Sel.Name)
if name[0] == '*' {
name = name[1:]
}
return name, true
}
return c.getIdentName(ident.Name, e.Sel.Name), false
}

View file

@ -147,3 +147,13 @@ func TestTypeConversionString(t *testing.T) {
}`
eval(t, src, []byte("lamao"))
}
func TestInterfaceTypeConversion(t *testing.T) {
src := `package foo
func Main() int {
a := 1
b := interface{}(a).(int)
return b
}`
eval(t, src, big.NewInt(1))
}

View file

@ -55,6 +55,15 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
copy(newScope, c.scope.vars.locals)
defer c.scope.vars.dropScope()
if f.decl.Recv != nil {
c.scope.vars.locals = newScope
name := f.decl.Recv.List[0].Names[0].Name
c.scope.vars.addAlias(name, -1, unspecifiedVarIndex, &varContext{
importMap: c.importMap,
expr: f.selector,
scope: oldScope,
})
}
hasVarArgs := !n.Ellipsis.IsValid()
needPack := sig.Variadic() && hasVarArgs
for i := range n.Args {

View file

@ -336,3 +336,41 @@ func TestPackageVarsInInlinedCalls(t *testing.T) {
}`
eval(t, src, big.NewInt(13))
}
func TestInlinedMethod(t *testing.T) {
src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/inline"
func Main() int {
// It's important for this variable to not be named 't'.
var z inline.T
i := z.Inc(42)
if i != 0 || z.N != 42 {
return 0
}
i = z.Inc(100500)
if i != 42 {
return 0
}
return z.N
}`
eval(t, src, big.NewInt(100542))
}
func TestInlinedMethodWithPointer(t *testing.T) {
src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/inline"
func Main() int {
// It's important for this variable to not be named 't'.
var z = &inline.T{}
i := z.Inc(42)
if i != 0 || z.N != 42 {
return 0
}
i = z.Inc(100500)
if i != 42 {
return 0
}
return z.N
}`
eval(t, src, big.NewInt(100542))
}

View file

@ -46,3 +46,13 @@ func SumVar(a, b int) int {
func Concat(n int) int {
return n*100 + b.A*10 + A
}
type T struct {
N int
}
func (t *T) Inc(i int) int {
n := t.N
t.N += i
return n
}

View file

@ -216,7 +216,7 @@ func (b *Block) GetExpectedBlockSizeWithoutTransactions(txCount int) int {
// ToStackItem converts Block to stackitem.Item.
func (b *Block) ToStackItem() stackitem.Item {
return stackitem.NewArray([]stackitem.Item{
items := []stackitem.Item{
stackitem.NewByteArray(b.Hash().BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(b.Version))),
stackitem.NewByteArray(b.PrevHash.BytesBE()),
@ -226,5 +226,10 @@ func (b *Block) ToStackItem() stackitem.Item {
stackitem.NewBigInteger(big.NewInt(int64(b.Index))),
stackitem.NewByteArray(b.NextConsensus.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(len(b.Transactions)))),
})
}
if b.StateRootEnabled {
items = append(items, stackitem.NewByteArray(b.PrevStateRoot.BytesBE()))
}
return stackitem.NewArray(items)
}

View file

@ -29,3 +29,27 @@ type Block struct {
// TransactionsLength represents the length of block's transactions array.
TransactionsLength int
}
// BlockSR is a stateroot-enabled Neo block. It's returned from the Ledger contract's
// GetBlock method when StateRootInHeader NeoGo extension is used. Use it only when
// you have it enabled when you need to access PrevStateRoot field, Block is sufficient
// otherwise. To get this data type ToBlockSR method of Block should be used. All of
// the fields are same as in Block except PrevStateRoot.
type BlockSR struct {
Hash interop.Hash256
Version int
PrevHash interop.Hash256
MerkleRoot interop.Hash256
Timestamp int
Nonce int
Index int
NextConsensus interop.Hash160
TransactionsLength int
// PrevStateRoot is a hash of the previous block's state root.
PrevStateRoot interop.Hash256
}
// ToBlockSR converts Block into BlockSR for chains with StateRootInHeader option.
func (b *Block) ToBlockSR() *BlockSR {
return interface{}(b).(*BlockSR)
}