Skip to content

Commit

Permalink
WIP: Add staking runtime messages
Browse files Browse the repository at this point in the history
  • Loading branch information
kostko committed Dec 1, 2020
1 parent 4cd417e commit 40f3501
Show file tree
Hide file tree
Showing 18 changed files with 316 additions and 43 deletions.
9 changes: 7 additions & 2 deletions go/consensus/tendermint/apps/roothash/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,10 @@ package api

type messageKind uint8

// RuntimeMessageNoop is the message kind used when dispatching Noop runtime messages.
var RuntimeMessageNoop = messageKind(0)
var (
// RuntimeMessageNoop is the message kind used when dispatching Noop runtime messages.
RuntimeMessageNoop = messageKind(0)

// RuntimeMessageStaking is the message kind used when dispatching Staking runtime messages.
RuntimeMessageStaking = messageKind(1)
)
10 changes: 9 additions & 1 deletion go/consensus/tendermint/apps/roothash/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,26 @@ import (
roothashApi "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/roothash/api"
roothash "github.com/oasisprotocol/oasis-core/go/roothash/api"
"github.com/oasisprotocol/oasis-core/go/roothash/api/block"
staking "github.com/oasisprotocol/oasis-core/go/staking/api"
)

func (app *rootHashApplication) processRuntimeMessages(
ctx *tmapi.Context,
rtState *roothash.RuntimeState,
msgs []block.Message,
) error {
// Prepare a new context for processing messages.
msgCtx := ctx.WithCallerAddress(staking.NewRuntimeAddress(rtState.Runtime.ID))
msgCtx.SetGasAccountant(tmapi.NewNopGasAccountant()) // Gas was already accounted for.
defer msgCtx.Close()

for i, msg := range msgs {
var err error
switch {
case msg.Noop != nil:
err = app.md.Publish(ctx, roothashApi.RuntimeMessageNoop, &msg.Noop)
err = app.md.Publish(msgCtx, roothashApi.RuntimeMessageNoop, msg.Noop)
case msg.Staking != nil:
err = app.md.Publish(msgCtx, roothashApi.RuntimeMessageStaking, msg.Staking)
default:
// Unsupported message.
err = roothash.ErrInvalidArgument
Expand Down
6 changes: 6 additions & 0 deletions go/consensus/tendermint/apps/roothash/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,12 @@ func (app *rootHashApplication) executorCommit(
return err
}

/*
msgGasAccountant := func(msg *block.Message) {
// TODO: Charging for gas requires each message to know how to charge for gas?
}
*/

for _, commit := range cc.Commits {
if err = rtState.ExecutorPool.AddExecutorCommitment(ctx, rtState.CurrentBlock, sv, nl, &commit); err != nil { // nolint: gosec
ctx.Logger().Error("failed to add compute commitment to round",
Expand Down
22 changes: 21 additions & 1 deletion go/consensus/tendermint/apps/staking/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import (
"github.com/oasisprotocol/oasis-core/go/consensus/api/transaction"
"github.com/oasisprotocol/oasis-core/go/consensus/tendermint/api"
registryState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/registry/state"
roothashApi "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/roothash/api"
stakingState "github.com/oasisprotocol/oasis-core/go/consensus/tendermint/apps/staking/state"
epochtime "github.com/oasisprotocol/oasis-core/go/epochtime/api"
"github.com/oasisprotocol/oasis-core/go/roothash/api/block"
staking "github.com/oasisprotocol/oasis-core/go/staking/api"
)

Expand Down Expand Up @@ -44,6 +46,9 @@ func (app *stakingApplication) Dependencies() []string {

func (app *stakingApplication) OnRegister(state api.ApplicationState, md api.MessageDispatcher) {
app.state = state

// Subscribe to messages emitted by other apps.
md.Subscribe(roothashApi.RuntimeMessageStaking, app)
}

func (app *stakingApplication) OnCleanup() {
Expand Down Expand Up @@ -99,7 +104,22 @@ func (app *stakingApplication) BeginBlock(ctx *api.Context, request types.Reques
}

func (app *stakingApplication) ExecuteMessage(ctx *api.Context, kind, msg interface{}) error {
return staking.ErrInvalidArgument
state := stakingState.NewMutableState(ctx.State())

switch kind {
case roothashApi.RuntimeMessageStaking:
m := msg.(*block.StakingMessage)
switch {
case m.Transfer != nil:
return app.transfer(ctx, state, m.Transfer)
case m.Withdraw != nil:
return app.withdraw(ctx, state, m.Withdraw)
default:
return staking.ErrInvalidArgument
}
default:
return staking.ErrInvalidArgument
}
}

func (app *stakingApplication) ExecuteTx(ctx *api.Context, tx *transaction.Transaction) error {
Expand Down
14 changes: 7 additions & 7 deletions go/consensus/tendermint/apps/staking/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (app *stakingApplication) transfer(ctx *api.Context, state *stakingState.Mu
return err
}

fromAddr := staking.NewAddress(ctx.TxSigner())
fromAddr := ctx.CallerAddress()
if fromAddr.IsReserved() || !isTransferPermitted(params, fromAddr) {
return staking.ErrForbidden
}
Expand Down Expand Up @@ -114,7 +114,7 @@ func (app *stakingApplication) burn(ctx *api.Context, state *stakingState.Mutabl
return err
}

fromAddr := staking.NewAddress(ctx.TxSigner())
fromAddr := ctx.CallerAddress()
if fromAddr.IsReserved() {
return staking.ErrForbidden
}
Expand Down Expand Up @@ -180,7 +180,7 @@ func (app *stakingApplication) addEscrow(ctx *api.Context, state *stakingState.M
return staking.ErrInvalidArgument
}

fromAddr := staking.NewAddress(ctx.TxSigner())
fromAddr := ctx.CallerAddress()
if fromAddr.IsReserved() {
return staking.ErrForbidden
}
Expand Down Expand Up @@ -272,7 +272,7 @@ func (app *stakingApplication) reclaimEscrow(ctx *api.Context, state *stakingSta
return err
}

toAddr := staking.NewAddress(ctx.TxSigner())
toAddr := ctx.CallerAddress()
if toAddr.IsReserved() {
return staking.ErrForbidden
}
Expand Down Expand Up @@ -396,7 +396,7 @@ func (app *stakingApplication) amendCommissionSchedule(
return err
}

fromAddr := staking.NewAddress(ctx.TxSigner())
fromAddr := ctx.CallerAddress()
if fromAddr.IsReserved() {
return staking.ErrForbidden
}
Expand Down Expand Up @@ -445,7 +445,7 @@ func (app *stakingApplication) allow(
}

// Validate addresses -- if either is reserved or both are equal, the method should fail.
addr := staking.NewAddress(ctx.TxSigner())
addr := ctx.CallerAddress()
if addr.IsReserved() || allow.Beneficiary.IsReserved() {
return staking.ErrForbidden
}
Expand Down Expand Up @@ -529,7 +529,7 @@ func (app *stakingApplication) withdraw(
}

// Validate addresses -- if either is reserved or both are equal, the method should fail.
toAddr := staking.NewAddress(ctx.TxSigner())
toAddr := ctx.CallerAddress()
if toAddr.IsReserved() || withdraw.From.IsReserved() {
return staking.ErrForbidden
}
Expand Down
98 changes: 88 additions & 10 deletions go/oasis-node/cmd/debug/txsource/workload/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
consensus "github.com/oasisprotocol/oasis-core/go/consensus/api"
runtimeClient "github.com/oasisprotocol/oasis-core/go/runtime/client/api"
runtimeTransaction "github.com/oasisprotocol/oasis-core/go/runtime/transaction"
staking "github.com/oasisprotocol/oasis-core/go/staking/api"
)

// NameRuntime is the name of the runtime workload.
Expand Down Expand Up @@ -45,18 +46,22 @@ const (
type runtimeRequest uint8

const (
runtimeRequestInsert runtimeRequest = 0
runtimeRequestGet runtimeRequest = 1
runtimeRequestRemove runtimeRequest = 2
runtimeRequestMessage runtimeRequest = 3
runtimeRequestInsert runtimeRequest = 0
runtimeRequestGet runtimeRequest = 1
runtimeRequestRemove runtimeRequest = 2
runtimeRequestMessage runtimeRequest = 3
runtimeRequestWithdraw runtimeRequest = 4
runtimeRequestTransfer runtimeRequest = 5
)

// Weights to select between requests types.
var runtimeRequestWeights = map[runtimeRequest]int{
runtimeRequestInsert: 2,
runtimeRequestGet: 1,
runtimeRequestRemove: 2,
runtimeRequestMessage: 1,
runtimeRequestInsert: 3,
runtimeRequestGet: 2,
runtimeRequestRemove: 3,
runtimeRequestMessage: 1,
runtimeRequestWithdraw: 1,
runtimeRequestTransfer: 1,
}

// RuntimeFlags are the runtime workload flags.
Expand All @@ -67,6 +72,8 @@ type runtime struct {

runtimeID common.Namespace
reckonedKeyValueState map[string]string

testAddress staking.Address
}

func (r *runtime) generateVal(rng *rand.Rand, existingKey bool) string {
Expand Down Expand Up @@ -311,9 +318,67 @@ func (r *runtime) doMessageRequest(ctx context.Context, rng *rand.Rand, rtc runt
return nil
}

func (r *runtime) doWithdrawRequest(ctx context.Context, rng *rand.Rand, rtc runtimeClient.RuntimeClient) error {
// Submit message request.
req := &runtimeTransaction.TxnCall{
Method: "consensus_withdraw",
Args: struct {
Withdraw staking.Withdraw `json:"withdraw"`
Nonce uint64 `json:"nonce"`
}{
Withdraw: staking.Withdraw{
From: r.testAddress,
Amount: *quantity.NewFromUint64(1),
},
Nonce: rng.Uint64(),
},
}
rsp, err := r.submitRuntimeRquest(ctx, rtc, req)
if err != nil {
r.Logger.Error("Submit withdraw request failure",
"request", req,
"err", err,
)
return fmt.Errorf("submit withdraw request failed: %w", err)
}

r.Logger.Debug("withdraw request success",
"request", req,
"response", rsp,
)
return nil
}

func (r *runtime) doTransferRequest(ctx context.Context, rng *rand.Rand, rtc runtimeClient.RuntimeClient) error {
// TODO
return nil
}

// Implements Workload.
func (r *runtime) NeedsFunds() bool {
return false
return true
}

func (r *runtime) initAccounts(ctx context.Context, fundingAccount signature.Signer) error {
// Allow the runtime to withdraw some funds from the funding account.
rtAddress := staking.NewRuntimeAddress(r.runtimeID)

tx := staking.NewAllowTx(0, nil, &staking.Allow{
Beneficiary: rtAddress,
AmountChange: *quantity.NewFromUint64(100000),
})
if err := r.FundSignAndSubmitTx(ctx, fundingAccount, tx); err != nil {
r.Logger.Error("failed to sign and submit allow transaction",
"tx", tx,
"signer", fundingAccount.Public(),
)
return fmt.Errorf("failed to sign and submit allow tx: %w", err)
}

// Configure the address used for runtime tests.
r.testAddress = staking.NewAddress(fundingAccount.Public())

return nil
}

// Implements Workload.
Expand Down Expand Up @@ -341,11 +406,16 @@ func (r *runtime) Run(
}
r.reckonedKeyValueState = make(map[string]string)

// Initialize staking accounts for testing runtime interactions.
if err = r.initAccounts(ctx, fundingAccount); err != nil {
return fmt.Errorf("failed to initialize accounts: %w", err)
}

// Set up the runtime client.
rtc := runtimeClient.NewRuntimeClient(conn)

// Wait for 2nd epoch, so that runtimes are up and running.
r.logger.Info("waiting for 2nd epoch")
r.Logger.Info("waiting for 2nd epoch")
if err := cnsc.WaitEpoch(ctx, 2); err != nil {
return fmt.Errorf("failed waiting for 2nd epoch: %w", err)
}
Expand Down Expand Up @@ -386,6 +456,14 @@ func (r *runtime) Run(
if err := r.doMessageRequest(ctx, rng, rtc); err != nil {
return fmt.Errorf("doMessageRequest failure: %w", err)
}
case runtimeRequestWithdraw:
if err := r.doWithdrawRequest(ctx, rng, rtc); err != nil {
return fmt.Errorf("doWithdrawRequest failure: %w", err)
}
case runtimeRequestTransfer:
if err := r.doTransferRequest(ctx, rng, rtc); err != nil {
return fmt.Errorf("doTransferRequest failure: %w", err)
}
default:
return fmt.Errorf("unimplemented")
}
Expand Down
29 changes: 28 additions & 1 deletion go/roothash/api/block/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,24 @@ package block
import (
"fmt"

"github.com/oasisprotocol/oasis-core/go/common/cbor"
"github.com/oasisprotocol/oasis-core/go/common/crypto/hash"
staking "github.com/oasisprotocol/oasis-core/go/staking/api"
)

// Message is a message that can be sent by a runtime.
type Message struct {
Noop *NoopMessage `json:"noop,omitempty"`
Noop *NoopMessage `json:"noop,omitempty"`
Staking *StakingMessage `json:"staking,omitempty"`
}

// ValidateBasic performs basic validation of the runtime message.
func (m *Message) ValidateBasic() error {
switch {
case m.Noop != nil:
return m.Noop.ValidateBasic()
case m.Staking != nil:
return m.Staking.ValidateBasic()
default:
return fmt.Errorf("runtime message has no fields set")
}
Expand All @@ -41,3 +46,25 @@ type NoopMessage struct {
func (nm *NoopMessage) ValidateBasic() error {
return nil
}

// StakingMessage is a runtime message that allows a runtime to perform staking operations.
type StakingMessage struct {
cbor.Versioned

Transfer *staking.Transfer `json:"transfer,omitempty"`
Withdraw *staking.Withdraw `json:"withdraw,omitempty"`
}

// ValidateBasic performs basic validation of the runtime message.
func (sm *StakingMessage) ValidateBasic() error {
switch {
case sm.Transfer != nil:
// No validation at this time.
return nil
case sm.Withdraw != nil:
// No validation at this time.
return nil
default:
return fmt.Errorf("staking runtime message has no fields set")
}
}
5 changes: 5 additions & 0 deletions go/roothash/api/block/message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/oasisprotocol/oasis-core/go/common/crypto/hash"
staking "github.com/oasisprotocol/oasis-core/go/staking/api"
)

func TestMessageHash(t *testing.T) {
Expand All @@ -18,6 +19,8 @@ func TestMessageHash(t *testing.T) {
{nil, "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"},
{[]Message{}, "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"},
{[]Message{{Noop: &NoopMessage{}}}, "c8b55f87109e30fe2ba57507ffc0e96e40df7c0d24dfef82a858632f5f8420f1"},
{[]Message{{Staking: &StakingMessage{Transfer: &staking.Transfer{}}}}, "a6b91f974b34a9192efd12025659a768520d2f04e1dae9839677456412cdb2be"},
{[]Message{{Staking: &StakingMessage{Withdraw: &staking.Withdraw{}}}}, "069b0fda76d804e3fd65d4bbd875c646f15798fb573ac613100df67f5ba4c3fd"},
} {
var h hash.Hash
err := h.UnmarshalHex(tc.expectedHash)
Expand All @@ -37,6 +40,8 @@ func TestMessageValidateBasic(t *testing.T) {
}{
{"NoFieldsSet", Message{}, false},
{"ValidNoop", Message{Noop: &NoopMessage{}}, true},
{"StakingNoFieldsSet", Message{Staking: &StakingMessage{}}, false},
{"ValidStaking", Message{Staking: &StakingMessage{Transfer: &staking.Transfer{}}}, true},
} {
err := tc.msg.ValidateBasic()
if tc.valid {
Expand Down
1 change: 1 addition & 0 deletions runtime/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ pub mod registry;
pub mod roothash;
pub mod runtime;
pub mod sgx;
pub mod staking;
pub mod time;
pub mod version;
Loading

0 comments on commit 40f3501

Please sign in to comment.