/
tx_executor_transfer.go
84 lines (75 loc) · 2.48 KB
/
tx_executor_transfer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package money
import (
"bytes"
"crypto"
"errors"
"fmt"
"github.com/alphabill-org/alphabill/state"
"github.com/alphabill-org/alphabill/txsystem"
"github.com/alphabill-org/alphabill/types"
)
var (
ErrInvalidDataType = errors.New("invalid data type")
ErrInvalidBillValue = errors.New("transaction value must be equal to bill value")
)
func (m *Module) handleTransferTx() txsystem.GenericExecuteFunc[TransferAttributes] {
return func(tx *types.TransactionOrder, attr *TransferAttributes, currentBlockNumber uint64) (*types.ServerMetadata, error) {
if err := m.validateTransferTx(tx, attr); err != nil {
return nil, fmt.Errorf("invalid transfer tx: %w", err)
}
// calculate actual tx fee cost
fee := m.feeCalculator()
// update state
updateDataFunc := updateBillDataFunc(tx, currentBlockNumber, m.hashAlgorithm)
setOwnerFunc := state.SetOwner(tx.UnitID(), attr.NewBearer)
if err := m.state.Apply(
setOwnerFunc,
updateDataFunc,
); err != nil {
return nil, fmt.Errorf("transfer: failed to update state: %w", err)
}
return &types.ServerMetadata{ActualFee: fee, TargetUnits: []types.UnitID{tx.UnitID()}, SuccessIndicator: types.TxStatusSuccessful}, nil
}
}
func (m *Module) validateTransferTx(tx *types.TransactionOrder, attr *TransferAttributes) error {
unit, err := m.state.GetUnit(tx.UnitID(), false)
if err != nil {
return err
}
if err := m.execPredicate(unit.Bearer(), tx.OwnerProof, tx); err != nil {
return fmt.Errorf("executing bearer predicate: %w", err)
}
return validateAnyTransfer(unit.Data(), attr.Backlink, attr.TargetValue)
}
func validateTransfer(data state.UnitData, attr *TransferAttributes) error {
return validateAnyTransfer(data, attr.Backlink, attr.TargetValue)
}
func validateAnyTransfer(data state.UnitData, backlink []byte, targetValue uint64) error {
bd, ok := data.(*BillData)
if !ok {
return ErrInvalidDataType
}
if bd.IsLocked() {
return ErrBillLocked
}
if !bytes.Equal(backlink, bd.Backlink) {
return ErrInvalidBacklink
}
if targetValue != bd.V {
return ErrInvalidBillValue
}
return nil
}
func updateBillDataFunc(tx *types.TransactionOrder, currentBlockNumber uint64, hashAlgorithm crypto.Hash) state.Action {
unitID := tx.UnitID()
return state.UpdateUnitData(unitID,
func(data state.UnitData) (state.UnitData, error) {
bd, ok := data.(*BillData)
if !ok {
return nil, fmt.Errorf("unit %v does not contain bill data", unitID)
}
bd.T = currentBlockNumber
bd.Backlink = tx.Hash(hashAlgorithm)
return bd, nil
})
}