Skip to content

Commit 6c617ef

Browse files
committed
introduce multiple result.Outputs and morpheus integration passes
1 parent 79e602d commit 6c617ef

File tree

8 files changed

+99
-62
lines changed

8 files changed

+99
-62
lines changed

chain/builder.go

+17-19
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ func BuildBlock(
294294
log.Warn("invalid tx: invalid state keys")
295295
return nil
296296
}
297-
txResults, err := tx.Execute(
297+
result, err := tx.Execute(
298298
ctx,
299299
feeManager,
300300
reads,
@@ -315,31 +315,29 @@ func BuildBlock(
315315
defer blockLock.Unlock()
316316

317317
// Ensure block isn't too big
318-
for _, result := range txResults {
319-
if ok, dimension := feeManager.Consume(result.Consumed, maxUnits); !ok {
320-
log.Debug(
321-
"skipping tx: too many units",
322-
zap.Int("dimension", int(dimension)),
323-
zap.Uint64("tx", result.Consumed[dimension]),
324-
zap.Uint64("block units", feeManager.LastConsumed(dimension)),
325-
zap.Uint64("max block units", maxUnits[dimension]),
326-
)
327-
restore = true
318+
if ok, dimension := feeManager.Consume(result.Consumed, maxUnits); !ok {
319+
log.Debug(
320+
"skipping tx: too many units",
321+
zap.Int("dimension", int(dimension)),
322+
zap.Uint64("tx", result.Consumed[dimension]),
323+
zap.Uint64("block units", feeManager.LastConsumed(dimension)),
324+
zap.Uint64("max block units", maxUnits[dimension]),
325+
)
326+
restore = true
328327

329-
// If we are above the target for the dimension we can't consume, we will
330-
// stop building. This prevents a full mempool iteration looking for the
331-
// "perfect fit".
332-
if feeManager.LastConsumed(dimension) >= targetUnits[dimension] {
333-
stop = true
334-
return errBlockFull
335-
}
328+
// If we are above the target for the dimension we can't consume, we will
329+
// stop building. This prevents a full mempool iteration looking for the
330+
// "perfect fit".
331+
if feeManager.LastConsumed(dimension) >= targetUnits[dimension] {
332+
stop = true
333+
return errBlockFull
336334
}
337335
}
338336

339337
// Update block with new transaction
340338
tsv.Commit()
341339
b.Txs = append(b.Txs, tx)
342-
results = append(results, txResults...)
340+
results = append(results, result)
343341
return nil
344342
})
345343
}

chain/dependencies.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -254,14 +254,16 @@ type Action interface {
254254
//
255255
// An error should only be returned if a fatal error was encountered, otherwise [success] should
256256
// be marked as false and fees will still be charged.
257+
//
258+
// TODO: Consider limiting number of outputs an [Action] can have
257259
Execute(
258260
ctx context.Context,
259261
r Rules,
260262
mu state.Mutable,
261263
timestamp int64,
262264
actor codec.Address,
263265
actionID codec.LID,
264-
) (success bool, computeUnits uint64, output []byte, err error)
266+
) (success bool, computeUnits uint64, outputs [][]byte, err error)
265267
}
266268

267269
type Auth interface {

chain/processor.go

+13-15
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ func (b *StatelessBlock) Execute(
3838
numTxs = len(b.Txs)
3939
t = b.GetTimestamp()
4040

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

4847
// Fetch required keys and execute transactions
49-
for _, ltx := range b.Txs {
48+
for li, ltx := range b.Txs {
49+
i := li
5050
tx := ltx
5151

5252
stateKeys, err := tx.StateKeys(sm)
@@ -79,18 +79,16 @@ func (b *StatelessBlock) Execute(
7979
return err
8080
}
8181

82-
txResults, err := tx.Execute(ctx, feeManager, reads, sm, r, tsv, t)
82+
result, err := tx.Execute(ctx, feeManager, reads, sm, r, tsv, t)
8383
if err != nil {
8484
return err
8585
}
86-
results = append(results, txResults...)
87-
88-
for _, result := range txResults {
89-
// Update block metadata with units actually consumed (if more is consumed than block allows, we will non-deterministically
90-
// exit with an error based on which tx over the limit is processed first)
91-
if ok, d := feeManager.Consume(result.Consumed, r.GetMaxBlockUnits()); !ok {
92-
return fmt.Errorf("%w: %d too large", ErrInvalidUnitsConsumed, d)
93-
}
86+
results[i] = result
87+
88+
// Update block metadata with units actually consumed (if more is consumed than block allows, we will non-deterministically
89+
// exit with an error based on which tx over the limit is processed first)
90+
if ok, d := feeManager.Consume(result.Consumed, r.GetMaxBlockUnits()); !ok {
91+
return fmt.Errorf("%w: %d too large", ErrInvalidUnitsConsumed, d)
9492
}
9593

9694
// Commit results to parent [TState]

chain/result.go

+37-6
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,37 @@ import (
1111

1212
type Result struct {
1313
Success bool
14-
Output []byte
14+
Outputs [][][]byte
1515

1616
Consumed fees.Dimensions
1717
Fee uint64
1818
}
1919

2020
func (r *Result) Size() int {
21-
return consts.BoolLen + codec.BytesLen(r.Output) + fees.DimensionsLen + consts.Uint64Len
21+
outputSize := consts.IntLen
22+
for _, action := range r.Outputs {
23+
for _, output := range action {
24+
outputSize += codec.BytesLen(output)
25+
}
26+
}
27+
return consts.BoolLen + outputSize + fees.DimensionsLen + consts.Uint64Len
2228
}
2329

2430
func (r *Result) Marshal(p *codec.Packer) error {
2531
p.PackBool(r.Success)
26-
p.PackBytes(r.Output)
32+
33+
numOutputs := 0
34+
for _, action := range r.Outputs {
35+
numOutputs += len(action)
36+
}
37+
p.PackInt(len(r.Outputs))
38+
p.PackInt(numOutputs)
39+
for _, action := range r.Outputs {
40+
for _, output := range action {
41+
p.PackBytes(output)
42+
}
43+
}
44+
2745
p.PackFixedBytes(r.Consumed.Bytes())
2846
p.PackUint64(r.Fee)
2947
return nil
@@ -45,10 +63,23 @@ func UnmarshalResult(p *codec.Packer) (*Result, error) {
4563
result := &Result{
4664
Success: p.UnpackBool(),
4765
}
48-
p.UnpackBytes(consts.MaxInt, false, &result.Output)
49-
if len(result.Output) == 0 {
66+
67+
totalOutputs := [][][]byte{}
68+
numActions := p.UnpackInt(false)
69+
numOutputs := p.UnpackInt(false)
70+
for i := 0; i < numActions; i++ {
71+
outputs := [][]byte{}
72+
for j := 0; j < numOutputs; j++ {
73+
var output []byte
74+
p.UnpackBytes(consts.MaxInt, false, &output)
75+
outputs = append(outputs, output)
76+
}
77+
totalOutputs = append(totalOutputs, outputs)
78+
}
79+
result.Outputs = totalOutputs
80+
if len(result.Outputs) == 0 {
5081
// Enforce object standardization
51-
result.Output = nil
82+
result.Outputs = nil
5283
}
5384
consumedRaw := make([]byte, fees.DimensionsLen)
5485
p.UnpackFixedBytes(fees.DimensionsLen, &consumedRaw)

chain/transaction.go

+22-15
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ func (t *Transaction) Execute(
294294
r Rules,
295295
ts *tstate.TStateView,
296296
timestamp int64,
297-
) ([]*Result, error) {
297+
) (*Result, error) {
298298
// Always charge fee first (in case [Action] moves funds)
299299
maxUnits, err := t.MaxUnits(s, r)
300300
if err != nil {
@@ -314,25 +314,28 @@ func (t *Transaction) Execute(
314314

315315
// We create a temp state checkpoint to ensure we don't commit failed actions to state.
316316
actionStart := ts.OpIndex()
317-
handleRevert := func(rerr error) ([]*Result, error) {
317+
handleRevert := func(rerr error) (*Result, error) {
318318
// Be warned that the variables captured in this function
319319
// are set when this function is defined. If any of them are
320320
// modified later, they will not be used here.
321321
ts.Rollback(ctx, actionStart)
322-
return []*Result{{false, utils.ErrBytes(rerr), maxUnits, maxFee}}, nil
322+
return &Result{false, [][][]byte{{utils.ErrBytes(rerr)}}, maxUnits, maxFee}, nil
323323
}
324-
results := make([]*Result, 0)
324+
resultOutputs := [][][]byte{}
325+
totalUsed := fees.Dimensions{}
326+
var totalFeeRequired uint64
325327
for i, action := range t.Actions {
326328
actionID := action.GetActionID(uint8(i), t.id)
327-
success, actionCUs, output, err := action.Execute(ctx, r, ts, timestamp, t.Auth.Actor(), actionID)
329+
success, actionCUs, outputs, err := action.Execute(ctx, r, ts, timestamp, t.Auth.Actor(), actionID)
328330
if err != nil {
329331
return handleRevert(err)
330332
}
331-
if len(output) == 0 && output != nil {
333+
if len(outputs) == 0 && outputs != nil {
332334
// Enforce object standardization (this is a VM bug and we should fail
333335
// fast)
334336
return handleRevert(ErrInvalidObject)
335337
}
338+
resultOutputs = append(resultOutputs, outputs)
336339
if !success {
337340
ts.Rollback(ctx, actionStart)
338341
}
@@ -392,6 +395,11 @@ func (t *Transaction) Execute(
392395
return handleRevert(err)
393396
}
394397
used := fees.Dimensions{uint64(t.Size()), computeUnits, readUnits, allocateUnits, writeUnits}
398+
nused, err := fees.Add(totalUsed, used)
399+
if err != nil {
400+
return handleRevert(err)
401+
}
402+
totalUsed = nused
395403

396404
// Check to see if the units consumed are greater than the max units
397405
//
@@ -408,6 +416,7 @@ func (t *Transaction) Execute(
408416
if err != nil {
409417
return handleRevert(err)
410418
}
419+
totalFeeRequired += feeRequired
411420
refund := maxFee - feeRequired
412421
if refund > 0 {
413422
ts.DisableAllocation()
@@ -416,16 +425,14 @@ func (t *Transaction) Execute(
416425
return handleRevert(err)
417426
}
418427
}
419-
420-
results = append(results, &Result{
421-
Success: success,
422-
Output: output,
423-
424-
Consumed: used,
425-
Fee: feeRequired,
426-
})
427428
}
428-
return results, nil
429+
return &Result{
430+
Success: true,
431+
Outputs: resultOutputs,
432+
433+
Consumed: totalUsed,
434+
Fee: totalFeeRequired,
435+
}, nil
429436
}
430437

431438
func (t *Transaction) Marshal(p *codec.Packer) error {

examples/morpheusvm/actions/transfer.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,17 @@ func (t *Transfer) Execute(
5252
_ int64,
5353
actor codec.Address,
5454
_ codec.LID,
55-
) (bool, uint64, []byte, error) {
55+
) (bool, uint64, [][]byte, error) {
5656
if t.Value == 0 {
57-
return false, 1, OutputValueZero, nil
57+
return false, 1, [][]byte{OutputValueZero}, nil
5858
}
5959
if err := storage.SubBalance(ctx, mu, actor, t.Value); err != nil {
60-
return false, 1, utils.ErrBytes(err), nil
60+
return false, 1, [][]byte{utils.ErrBytes(err)}, nil
6161
}
6262
if err := storage.AddBalance(ctx, mu, t.To, t.Value, true); err != nil {
63-
return false, 1, utils.ErrBytes(err), nil
63+
return false, 1, [][]byte{utils.ErrBytes(err)}, nil
6464
}
65-
return true, 1, nil, nil
65+
return true, 1, [][]byte{{}}, nil
6666
}
6767

6868
func (*Transfer) MaxComputeUnits(chain.Rules) uint64 {

examples/morpheusvm/controller/controller.go

+1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ func (c *Controller) Accepted(ctx context.Context, blk *chain.StatelessBlock) er
153153
results := blk.Results()
154154
for i, tx := range blk.Txs {
155155
result := results[i]
156+
fmt.Printf("result %v | results %v\n", result, results)
156157
if c.config.GetStoreTransactions() {
157158
err := storage.StoreTransaction(
158159
ctx,

examples/morpheusvm/tests/integration/integration_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() {
423423
results := blk.(*chain.StatelessBlock).Results()
424424
gomega.Ω(results).Should(gomega.HaveLen(1))
425425
gomega.Ω(results[0].Success).Should(gomega.BeTrue())
426-
gomega.Ω(results[0].Output).Should(gomega.BeNil())
426+
gomega.Ω(len(results[0].Outputs[0])).To(gomega.Equal(1))
427427

428428
// Unit explanation
429429
//

0 commit comments

Comments
 (0)