Merge pull request #2583 from nspcc-dev/getblock-sr
Stateroot-enabled GetBlock
This commit is contained in:
commit
34ddc99a08
9 changed files with 121 additions and 12 deletions
|
@ -25,6 +25,9 @@ a dialect of Go rather than a complete port of the language:
|
||||||
in variables and returning the result.
|
in variables and returning the result.
|
||||||
* lambdas are supported, but closures are not.
|
* lambdas are supported, but closures are not.
|
||||||
* maps are supported, but valid map keys are booleans, integers and strings with length <= 64
|
* 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)
|
## VM API (interop layer)
|
||||||
Compiler translates interop function calls into NEO VM syscalls or (for custom
|
Compiler translates interop function calls into NEO VM syscalls or (for custom
|
||||||
|
|
|
@ -371,7 +371,8 @@ func canConvert(s string) bool {
|
||||||
s = s[len(interopPrefix):]
|
s = s[len(interopPrefix):]
|
||||||
return s != "/iterator.Iterator" && s != "/storage.Context" &&
|
return s != "/iterator.Iterator" && s != "/storage.Context" &&
|
||||||
s != "/native/ledger.Block" && s != "/native/ledger.Transaction" &&
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -914,15 +914,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case *ast.SelectorExpr:
|
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)
|
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]
|
f, ok = c.funcs[name]
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -938,12 +930,25 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
c.emitExplicitConvert(c.typeOf(n.Args[0]), typ)
|
c.emitExplicitConvert(c.typeOf(n.Args[0]), typ)
|
||||||
return nil
|
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:
|
case *ast.ArrayType:
|
||||||
// For now we will assume that there are only byte slice conversions.
|
// For now we will assume that there are only byte slice conversions.
|
||||||
// E.g. []byte("foobar") or []byte(scriptHash).
|
// E.g. []byte("foobar") or []byte(scriptHash).
|
||||||
ast.Walk(c, n.Args[0])
|
ast.Walk(c, n.Args[0])
|
||||||
c.emitConvert(stackitem.BufferT)
|
c.emitConvert(stackitem.BufferT)
|
||||||
return nil
|
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:
|
case *ast.FuncLit:
|
||||||
isLiteral = true
|
isLiteral = true
|
||||||
}
|
}
|
||||||
|
@ -2067,7 +2072,11 @@ func (c *codegen) getFuncNameFromSelector(e *ast.SelectorExpr) (string, bool) {
|
||||||
ident := e.X.(*ast.Ident)
|
ident := e.X.(*ast.Ident)
|
||||||
if c.typeInfo.Selections[e] != nil {
|
if c.typeInfo.Selections[e] != nil {
|
||||||
typ := c.typeInfo.Types[ident].Type.String()
|
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
|
return c.getIdentName(ident.Name, e.Sel.Name), false
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,3 +147,13 @@ func TestTypeConversionString(t *testing.T) {
|
||||||
}`
|
}`
|
||||||
eval(t, src, []byte("lamao"))
|
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))
|
||||||
|
}
|
||||||
|
|
|
@ -55,6 +55,15 @@ func (c *codegen) inlineCall(f *funcScope, n *ast.CallExpr) {
|
||||||
copy(newScope, c.scope.vars.locals)
|
copy(newScope, c.scope.vars.locals)
|
||||||
defer c.scope.vars.dropScope()
|
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()
|
hasVarArgs := !n.Ellipsis.IsValid()
|
||||||
needPack := sig.Variadic() && hasVarArgs
|
needPack := sig.Variadic() && hasVarArgs
|
||||||
for i := range n.Args {
|
for i := range n.Args {
|
||||||
|
|
|
@ -336,3 +336,41 @@ func TestPackageVarsInInlinedCalls(t *testing.T) {
|
||||||
}`
|
}`
|
||||||
eval(t, src, big.NewInt(13))
|
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))
|
||||||
|
}
|
||||||
|
|
10
pkg/compiler/testdata/inline/inline.go
vendored
10
pkg/compiler/testdata/inline/inline.go
vendored
|
@ -46,3 +46,13 @@ func SumVar(a, b int) int {
|
||||||
func Concat(n int) int {
|
func Concat(n int) int {
|
||||||
return n*100 + b.A*10 + A
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -216,7 +216,7 @@ func (b *Block) GetExpectedBlockSizeWithoutTransactions(txCount int) int {
|
||||||
|
|
||||||
// ToStackItem converts Block to stackitem.Item.
|
// ToStackItem converts Block to stackitem.Item.
|
||||||
func (b *Block) ToStackItem() stackitem.Item {
|
func (b *Block) ToStackItem() stackitem.Item {
|
||||||
return stackitem.NewArray([]stackitem.Item{
|
items := []stackitem.Item{
|
||||||
stackitem.NewByteArray(b.Hash().BytesBE()),
|
stackitem.NewByteArray(b.Hash().BytesBE()),
|
||||||
stackitem.NewBigInteger(big.NewInt(int64(b.Version))),
|
stackitem.NewBigInteger(big.NewInt(int64(b.Version))),
|
||||||
stackitem.NewByteArray(b.PrevHash.BytesBE()),
|
stackitem.NewByteArray(b.PrevHash.BytesBE()),
|
||||||
|
@ -226,5 +226,10 @@ func (b *Block) ToStackItem() stackitem.Item {
|
||||||
stackitem.NewBigInteger(big.NewInt(int64(b.Index))),
|
stackitem.NewBigInteger(big.NewInt(int64(b.Index))),
|
||||||
stackitem.NewByteArray(b.NextConsensus.BytesBE()),
|
stackitem.NewByteArray(b.NextConsensus.BytesBE()),
|
||||||
stackitem.NewBigInteger(big.NewInt(int64(len(b.Transactions)))),
|
stackitem.NewBigInteger(big.NewInt(int64(len(b.Transactions)))),
|
||||||
})
|
}
|
||||||
|
if b.StateRootEnabled {
|
||||||
|
items = append(items, stackitem.NewByteArray(b.PrevStateRoot.BytesBE()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return stackitem.NewArray(items)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,3 +29,27 @@ type Block struct {
|
||||||
// TransactionsLength represents the length of block's transactions array.
|
// TransactionsLength represents the length of block's transactions array.
|
||||||
TransactionsLength int
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue