Skip to content

Commit

Permalink
introduce multiple result.Outputs and morpheus integration passes
Browse files Browse the repository at this point in the history
  • Loading branch information
wlawt committed Apr 28, 2024
1 parent 79e602d commit 6c617ef
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 62 deletions.
36 changes: 17 additions & 19 deletions chain/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ func BuildBlock(
log.Warn("invalid tx: invalid state keys")
return nil
}
txResults, err := tx.Execute(
result, err := tx.Execute(
ctx,
feeManager,
reads,
Expand All @@ -315,31 +315,29 @@ func BuildBlock(
defer blockLock.Unlock()

// Ensure block isn't too big
for _, result := range txResults {
if ok, dimension := feeManager.Consume(result.Consumed, maxUnits); !ok {
log.Debug(
"skipping tx: too many units",
zap.Int("dimension", int(dimension)),
zap.Uint64("tx", result.Consumed[dimension]),
zap.Uint64("block units", feeManager.LastConsumed(dimension)),
zap.Uint64("max block units", maxUnits[dimension]),
)
restore = true
if ok, dimension := feeManager.Consume(result.Consumed, maxUnits); !ok {
log.Debug(
"skipping tx: too many units",
zap.Int("dimension", int(dimension)),
zap.Uint64("tx", result.Consumed[dimension]),
zap.Uint64("block units", feeManager.LastConsumed(dimension)),
zap.Uint64("max block units", maxUnits[dimension]),
)
restore = true

// If we are above the target for the dimension we can't consume, we will
// stop building. This prevents a full mempool iteration looking for the
// "perfect fit".
if feeManager.LastConsumed(dimension) >= targetUnits[dimension] {
stop = true
return errBlockFull
}
// If we are above the target for the dimension we can't consume, we will
// stop building. This prevents a full mempool iteration looking for the
// "perfect fit".
if feeManager.LastConsumed(dimension) >= targetUnits[dimension] {
stop = true
return errBlockFull
}
}

// Update block with new transaction
tsv.Commit()
b.Txs = append(b.Txs, tx)
results = append(results, txResults...)
results = append(results, result)
return nil
})
}
Expand Down
4 changes: 3 additions & 1 deletion chain/dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,14 +254,16 @@ type Action interface {
//
// An error should only be returned if a fatal error was encountered, otherwise [success] should
// be marked as false and fees will still be charged.
//
// TODO: Consider limiting number of outputs an [Action] can have
Execute(
ctx context.Context,
r Rules,
mu state.Mutable,
timestamp int64,
actor codec.Address,
actionID codec.LID,
) (success bool, computeUnits uint64, output []byte, err error)
) (success bool, computeUnits uint64, outputs [][]byte, err error)
}

type Auth interface {
Expand Down
28 changes: 13 additions & 15 deletions chain/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ func (b *StatelessBlock) Execute(
numTxs = len(b.Txs)
t = b.GetTimestamp()

f = fetcher.New(im, numTxs, b.vm.GetStateFetchConcurrency())
e = executor.New(numTxs, b.vm.GetTransactionExecutionCores(), MaxKeyDependencies, b.vm.GetExecutorVerifyRecorder())
ts = tstate.New(numTxs * 2) // TODO: tune this heuristic
// TODO: specify len?
results = []*Result{}
f = fetcher.New(im, numTxs, b.vm.GetStateFetchConcurrency())
e = executor.New(numTxs, b.vm.GetTransactionExecutionCores(), MaxKeyDependencies, b.vm.GetExecutorVerifyRecorder())
ts = tstate.New(numTxs * 2) // TODO: tune this heuristic
results = make([]*Result, numTxs)
)

// Fetch required keys and execute transactions
for _, ltx := range b.Txs {
for li, ltx := range b.Txs {
i := li
tx := ltx

stateKeys, err := tx.StateKeys(sm)
Expand Down Expand Up @@ -79,18 +79,16 @@ func (b *StatelessBlock) Execute(
return err
}

txResults, err := tx.Execute(ctx, feeManager, reads, sm, r, tsv, t)
result, err := tx.Execute(ctx, feeManager, reads, sm, r, tsv, t)
if err != nil {
return err
}
results = append(results, txResults...)

for _, result := range txResults {
// Update block metadata with units actually consumed (if more is consumed than block allows, we will non-deterministically
// exit with an error based on which tx over the limit is processed first)
if ok, d := feeManager.Consume(result.Consumed, r.GetMaxBlockUnits()); !ok {
return fmt.Errorf("%w: %d too large", ErrInvalidUnitsConsumed, d)
}
results[i] = result

// Update block metadata with units actually consumed (if more is consumed than block allows, we will non-deterministically
// exit with an error based on which tx over the limit is processed first)
if ok, d := feeManager.Consume(result.Consumed, r.GetMaxBlockUnits()); !ok {
return fmt.Errorf("%w: %d too large", ErrInvalidUnitsConsumed, d)
}

// Commit results to parent [TState]
Expand Down
43 changes: 37 additions & 6 deletions chain/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,37 @@ import (

type Result struct {
Success bool
Output []byte
Outputs [][][]byte

Consumed fees.Dimensions
Fee uint64
}

func (r *Result) Size() int {
return consts.BoolLen + codec.BytesLen(r.Output) + fees.DimensionsLen + consts.Uint64Len
outputSize := consts.IntLen
for _, action := range r.Outputs {
for _, output := range action {
outputSize += codec.BytesLen(output)
}
}
return consts.BoolLen + outputSize + fees.DimensionsLen + consts.Uint64Len
}

func (r *Result) Marshal(p *codec.Packer) error {
p.PackBool(r.Success)
p.PackBytes(r.Output)

numOutputs := 0
for _, action := range r.Outputs {
numOutputs += len(action)
}
p.PackInt(len(r.Outputs))
p.PackInt(numOutputs)
for _, action := range r.Outputs {
for _, output := range action {
p.PackBytes(output)
}
}

p.PackFixedBytes(r.Consumed.Bytes())
p.PackUint64(r.Fee)
return nil
Expand All @@ -45,10 +63,23 @@ func UnmarshalResult(p *codec.Packer) (*Result, error) {
result := &Result{
Success: p.UnpackBool(),
}
p.UnpackBytes(consts.MaxInt, false, &result.Output)
if len(result.Output) == 0 {

totalOutputs := [][][]byte{}
numActions := p.UnpackInt(false)
numOutputs := p.UnpackInt(false)
for i := 0; i < numActions; i++ {
outputs := [][]byte{}
for j := 0; j < numOutputs; j++ {
var output []byte
p.UnpackBytes(consts.MaxInt, false, &output)
outputs = append(outputs, output)
}
totalOutputs = append(totalOutputs, outputs)
}
result.Outputs = totalOutputs
if len(result.Outputs) == 0 {
// Enforce object standardization
result.Output = nil
result.Outputs = nil
}
consumedRaw := make([]byte, fees.DimensionsLen)
p.UnpackFixedBytes(fees.DimensionsLen, &consumedRaw)
Expand Down
37 changes: 22 additions & 15 deletions chain/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ func (t *Transaction) Execute(
r Rules,
ts *tstate.TStateView,
timestamp int64,
) ([]*Result, error) {
) (*Result, error) {
// Always charge fee first (in case [Action] moves funds)
maxUnits, err := t.MaxUnits(s, r)
if err != nil {
Expand All @@ -314,25 +314,28 @@ func (t *Transaction) Execute(

// We create a temp state checkpoint to ensure we don't commit failed actions to state.
actionStart := ts.OpIndex()
handleRevert := func(rerr error) ([]*Result, error) {
handleRevert := func(rerr error) (*Result, error) {
// Be warned that the variables captured in this function
// are set when this function is defined. If any of them are
// modified later, they will not be used here.
ts.Rollback(ctx, actionStart)
return []*Result{{false, utils.ErrBytes(rerr), maxUnits, maxFee}}, nil
return &Result{false, [][][]byte{{utils.ErrBytes(rerr)}}, maxUnits, maxFee}, nil
}
results := make([]*Result, 0)
resultOutputs := [][][]byte{}
totalUsed := fees.Dimensions{}
var totalFeeRequired uint64
for i, action := range t.Actions {
actionID := action.GetActionID(uint8(i), t.id)
success, actionCUs, output, err := action.Execute(ctx, r, ts, timestamp, t.Auth.Actor(), actionID)
success, actionCUs, outputs, err := action.Execute(ctx, r, ts, timestamp, t.Auth.Actor(), actionID)
if err != nil {
return handleRevert(err)
}
if len(output) == 0 && output != nil {
if len(outputs) == 0 && outputs != nil {
// Enforce object standardization (this is a VM bug and we should fail
// fast)
return handleRevert(ErrInvalidObject)
}
resultOutputs = append(resultOutputs, outputs)
if !success {
ts.Rollback(ctx, actionStart)
}
Expand Down Expand Up @@ -392,6 +395,11 @@ func (t *Transaction) Execute(
return handleRevert(err)
}
used := fees.Dimensions{uint64(t.Size()), computeUnits, readUnits, allocateUnits, writeUnits}
nused, err := fees.Add(totalUsed, used)
if err != nil {
return handleRevert(err)
}
totalUsed = nused

// Check to see if the units consumed are greater than the max units
//
Expand All @@ -408,6 +416,7 @@ func (t *Transaction) Execute(
if err != nil {
return handleRevert(err)
}
totalFeeRequired += feeRequired
refund := maxFee - feeRequired
if refund > 0 {
ts.DisableAllocation()
Expand All @@ -416,16 +425,14 @@ func (t *Transaction) Execute(
return handleRevert(err)
}
}

results = append(results, &Result{
Success: success,
Output: output,

Consumed: used,
Fee: feeRequired,
})
}
return results, nil
return &Result{
Success: true,
Outputs: resultOutputs,

Consumed: totalUsed,
Fee: totalFeeRequired,
}, nil
}

func (t *Transaction) Marshal(p *codec.Packer) error {
Expand Down
10 changes: 5 additions & 5 deletions examples/morpheusvm/actions/transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,17 @@ func (t *Transfer) Execute(
_ int64,
actor codec.Address,
_ codec.LID,
) (bool, uint64, []byte, error) {
) (bool, uint64, [][]byte, error) {
if t.Value == 0 {
return false, 1, OutputValueZero, nil
return false, 1, [][]byte{OutputValueZero}, nil
}
if err := storage.SubBalance(ctx, mu, actor, t.Value); err != nil {
return false, 1, utils.ErrBytes(err), nil
return false, 1, [][]byte{utils.ErrBytes(err)}, nil
}
if err := storage.AddBalance(ctx, mu, t.To, t.Value, true); err != nil {
return false, 1, utils.ErrBytes(err), nil
return false, 1, [][]byte{utils.ErrBytes(err)}, nil
}
return true, 1, nil, nil
return true, 1, [][]byte{{}}, nil
}

func (*Transfer) MaxComputeUnits(chain.Rules) uint64 {
Expand Down
1 change: 1 addition & 0 deletions examples/morpheusvm/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ func (c *Controller) Accepted(ctx context.Context, blk *chain.StatelessBlock) er
results := blk.Results()
for i, tx := range blk.Txs {
result := results[i]
fmt.Printf("result %v | results %v\n", result, results)
if c.config.GetStoreTransactions() {
err := storage.StoreTransaction(
ctx,
Expand Down
2 changes: 1 addition & 1 deletion examples/morpheusvm/tests/integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() {
results := blk.(*chain.StatelessBlock).Results()
gomega.Ω(results).Should(gomega.HaveLen(1))
gomega.Ω(results[0].Success).Should(gomega.BeTrue())
gomega.Ω(results[0].Output).Should(gomega.BeNil())
gomega.Ω(len(results[0].Outputs[0])).To(gomega.Equal(1))

// Unit explanation
//
Expand Down

0 comments on commit 6c617ef

Please sign in to comment.