Skip to content

Commit

Permalink
vm: implementation that supports account abstraction model (includes …
Browse files Browse the repository at this point in the history
…signle key wallet) (#3229)

related #3220

- state

account extend with Template and State fields.
stub accounts will have both Template and State as nil. When account is spawned with template - Template will store the address of the template, and State will store mutable and immutable state encoded using scale.

- address

when account is spawned address is computed as `sha256(template_address, spawn_args)`. this must be taken into an account when configuring the coinbase for a node.

- validation and mempool

tx id is computed as a `sha256(raw_tx)`, note that raw_tx is a full body of the transaction including signature.
tx header can't be decoded without executing template code (specifically parse_payload and max_spend methods). 

in the upcoming pr - mempool will call into vm to initialize a validation flow (which includes parsing of the tx header). validation api is a 2 step process:
- parse. returns transaction header that can be validated against conservative state
- verify. executes templates verify method. it is assumed that this method costs much more than parse, so it makes sense to call it only if conservative validation succeeded.

- applying transaction

vm.Apply API method accepts layer id, transactions and rewards. Every transaction loads state, executes parse payload, initializes state and executed. Template/Handler API is specified in `genvm/core/types.go`.

Transaction can be skipped for 3 reasons:
- transaction is malformed (expectation is that such transactions should not be added to the block at all)
- account is not spawned (same as a nonce mismatch)
- nonce doesn't match the expected nonce of the account
- can't cover max gas with the account balance

If transaction is skipped it is returned back to the caller (presumably conservative state and the caller is free to do anything about it). Otherwise transaction is consumed and will pay fee to smeshers (in the followup pr for every consumed transaction we will store execution result). Logic for the fee distribution is not changed.
  • Loading branch information
dshulyak committed Jun 11, 2022
1 parent a548a90 commit 3613b9f
Show file tree
Hide file tree
Showing 41 changed files with 2,557 additions and 51 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: CI

env:
go-version: '1.17.6'
go-version: '1.18.2'
GCLOUD_KEY: ${{ secrets.GCLOUD_KEY }}
PROJECT_NAME: ${{ secrets.PROJECT_NAME }}
CLUSTER_NAME: ${{ secrets.CLUSTER_NAME }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v1
with:
go-version: '1.17.6'
go-version: '1.18.2'

- if: matrix.os == 'windows-latest'
name: Install dependencies in windows
Expand Down
6 changes: 3 additions & 3 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ output:
linters-settings:
staticcheck:
# Select the Go version to target. The default is '1.13'.
go: "1.17"
go: "1.18"
# https://staticcheck.io/docs/options#checks
checks: [ "all" ]

Expand Down Expand Up @@ -161,14 +161,14 @@ linters-settings:
# comments to be checked: `declarations`, `toplevel`, or `all`
scope: declarations
# list of regexps for excluding particular comment lines from check
exclude:
exclude: []
# example: exclude comments which contain numbers
# - '[0-9]+'
# check that each sentence starts with a capital letter
capital: false
gofumpt:
# Select the Go version to target. The default is `1.15`.
lang-version: "1.17"
lang-version: "1.18"
# Choose whether or not to use the extra rules that are disabled
# by default
extra-rules: false
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ LABEL com.nvidia.volumes.needed="nvidia_driver"
FROM linux as golang
ARG TARGETPLATFORM
ENV GOLANG_MAJOR_VERSION 1
ENV GOLANG_MINOR_VERSION 17
ENV GOLANG_PATCH_VERSION 6
ENV GOLANG_MINOR_VERSION 18
ENV GOLANG_PATCH_VERSION 1
ENV GOLANG_VERSION $GOLANG_MAJOR_VERSION.$GOLANG_MINOR_VERSION.$GOLANG_PATCH_VERSION
ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ endif


install:
go run scripts/check-go-version.go --major 1 --minor 17
go run scripts/check-go-version.go --major 1 --minor 18
go mod download
GO111MODULE=off go get golang.org/x/lint/golint
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.45.2
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Since the project uses Go Modules it is best to place the code **outside** your

Building is supported on OS X, Linux, FreeBSD, and Windows.

Install [Go 1.17 or later](https://golang.org/dl/) for your platform, if you haven't already.
Install [Go 1.18 or later](https://golang.org/dl/) for your platform, if you haven't already.

On Windows you need to install `make` via [msys2](https://www.msys2.org/), [MingGW-w64](http://mingw-w64.org/doku.php) or [mingw] (https://chocolatey.org/packages/mingw)

Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ clone_folder: c:\gopath\src\github.com\spacemeshos\go-spacemesh\
environment:
GOPATH: c:\gopath

stack: go 1.17
stack: go 1.18

before_build:
- choco install make
Expand Down
29 changes: 29 additions & 0 deletions common/types/account.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
package types

import (
"github.com/spacemeshos/go-spacemesh/log"
)

//go:generate scalegen

// Account represents account state at a certain layer.
type Account struct {
Layer LayerID
Address Address
Initialized bool
Nonce uint64
Balance uint64
Template *Address
State []byte
}

// NextNonce returns next expected nonce for the account state.
func (a *Account) NextNonce() uint64 {
if a.Template == nil {
return 0
}
return a.Nonce + 1
}

// MarshalLogObject implements encoding for the account state.
func (a *Account) MarshalLogObject(encoder log.ObjectEncoder) error {
encoder.AddString("layer", a.Layer.String())
encoder.AddString("principal", a.Address.String())
encoder.AddUint64("nonce", a.Nonce)
encoder.AddUint64("next nonce", a.NextNonce())
encoder.AddUint64("balance", a.Balance)
if a.Template != nil {
encoder.AddString("template", a.Template.String())
}
return nil
}
90 changes: 90 additions & 0 deletions common/types/account_scale.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 14 additions & 3 deletions common/types/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"math/big"

"github.com/spacemeshos/go-scale"

"github.com/spacemeshos/go-spacemesh/common/util"
"github.com/spacemeshos/go-spacemesh/hash"
"github.com/spacemeshos/go-spacemesh/log"
Expand Down Expand Up @@ -48,11 +50,11 @@ func (a Address) Hex() string {
unchecksummed := hex.EncodeToString(a[:])
sha := hash.New()
sha.Write([]byte(unchecksummed))
hash := sha.Sum(nil)
hh := sha.Sum(nil)

result := []byte(unchecksummed)
for i := 0; i < len(result); i++ {
hashByte := hash[i/2]
hashByte := hh[i/2]
if i%2 == 0 {
hashByte = hashByte >> 4
} else {
Expand Down Expand Up @@ -94,10 +96,19 @@ func (a *Address) SetBytes(b []byte) {
copy(a[AddressLength-len(b):], b)
}

// EncodeScale implements scale codec interface.
func (a *Address) EncodeScale(e *scale.Encoder) (int, error) {
return scale.EncodeByteArray(e, a[:])
}

// DecodeScale implements scale codec interface.
func (a *Address) DecodeScale(d *scale.Decoder) (int, error) {
return scale.DecodeByteArray(d, a[:])
}

// GenerateAddress generates an address from a public key.
func GenerateAddress(publicKey []byte) Address {
var addr Address
addr.SetBytes(publicKey)

return addr
}
16 changes: 16 additions & 0 deletions common/types/bytes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package types

import "github.com/spacemeshos/go-scale"

// Bytes64 is 64 byte array.
type Bytes64 [64]byte

// EncodeScale implements scale codec interface.
func (b *Bytes64) EncodeScale(encoder *scale.Encoder) (int, error) {
return scale.EncodeByteArray(encoder, b[:])
}

// DecodeScale implements scale codec interface.
func (b *Bytes64) DecodeScale(decoder *scale.Decoder) (int, error) {
return scale.DecodeByteArray(decoder, b[:])
}
34 changes: 11 additions & 23 deletions common/types/hashes.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package types
import (
"fmt"
"math/big"
"math/rand"
"reflect"

"github.com/spacemeshos/go-scale"

"github.com/spacemeshos/go-spacemesh/codec"
"github.com/spacemeshos/go-spacemesh/common/util"
"github.com/spacemeshos/go-spacemesh/hash"
Expand Down Expand Up @@ -63,7 +64,6 @@ func (h *Hash20) UnmarshalText(input []byte) error {
if err := util.UnmarshalFixedText("Hash", input, h[:]); err != nil {
return fmt.Errorf("unmarshal text: %w", err)
}

return nil
}

Expand Down Expand Up @@ -263,27 +263,15 @@ func (h Hash32) ToHash20() (h20 Hash20) {
return
}

// Generate implements testing/quick.Generator.
func (h Hash32) Generate(rand *rand.Rand, _ int) reflect.Value {
m := rand.Intn(len(h))
for i := len(h) - 1; i > m; i-- {
h[i] = byte(rand.Uint32())
}
return reflect.ValueOf(h)
}
// Field returns a log field. Implements the LoggableField interface.
func (h Hash32) Field() log.Field { return log.String("hash", util.Bytes2Hex(h[:])) }

// Scan implements Scanner for database/sql.
func (h *Hash32) Scan(src interface{}) error {
srcB, ok := src.([]byte)
if !ok {
return fmt.Errorf("can't scan %T into Hash", src)
}
if len(srcB) != Hash32Length {
return fmt.Errorf("can't scan []byte of len %d into Hash, want %d", len(srcB), Hash32Length)
}
copy(h[:], srcB)
return nil
// EncodeScale implements scale codec interface.
func (h *Hash32) EncodeScale(e *scale.Encoder) (int, error) {
return scale.EncodeByteArray(e, h[:])
}

// Field returns a log field. Implements the LoggableField interface.
func (h Hash32) Field() log.Field { return log.String("hash", util.Bytes2Hex(h[:])) }
// DecodeScale implements scale codec interface.
func (h *Hash32) DecodeScale(d *scale.Decoder) (int, error) {
return scale.DecodeByteArray(d, h[:])
}
2 changes: 2 additions & 0 deletions common/types/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ func NewLayerID(value uint32) LayerID {
return LayerID{Value: value}
}

//go:generate scalegen -types LayerID

// LayerID is representing a layer number. Zero value is safe to use, and means 0.
// Internally it is a simple wrapper over uint32 and should be considered immutable
// the same way as any integer.
Expand Down
26 changes: 26 additions & 0 deletions common/types/layer_scale.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions common/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/spacemeshos/ed25519"

"github.com/spacemeshos/go-spacemesh/hash"
"github.com/spacemeshos/go-spacemesh/log"
)

Expand Down Expand Up @@ -232,3 +233,17 @@ type Reward struct {
LayerReward uint64
Coinbase Address
}

// NewRawTx computes id from raw bytes and returns the object.
func NewRawTx(raw []byte) RawTx {
return RawTx{
ID: hash.Sum(raw),
Raw: raw,
}
}

// RawTx stores an identity and a pointer to raw bytes.
type RawTx struct {
ID TransactionID
Raw []byte
}
Loading

0 comments on commit 3613b9f

Please sign in to comment.