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.
|
||||
* 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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
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 {
|
||||
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.
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue