154 lines
3.9 KiB
Go
154 lines
3.9 KiB
Go
|
package builders
|
||
|
|
||
|
import (
|
||
|
"github.com/filecoin-project/go-address"
|
||
|
"github.com/filecoin-project/lotus/chain/types"
|
||
|
"github.com/filecoin-project/lotus/chain/vm"
|
||
|
"github.com/filecoin-project/specs-actors/actors/abi"
|
||
|
"github.com/filecoin-project/specs-actors/actors/abi/big"
|
||
|
)
|
||
|
|
||
|
// TypedCall represents a call to a known built-in actor kind.
|
||
|
type TypedCall func() (method abi.MethodNum, params []byte)
|
||
|
|
||
|
// Messages accumulates the messages to be executed within the test vector.
|
||
|
type Messages struct {
|
||
|
b *Builder
|
||
|
defaults msgOpts
|
||
|
|
||
|
messages []*ApplicableMessage
|
||
|
}
|
||
|
|
||
|
// SetDefaults sets default options for all messages.
|
||
|
func (m *Messages) SetDefaults(opts ...MsgOpt) *Messages {
|
||
|
for _, opt := range opts {
|
||
|
opt(&m.defaults)
|
||
|
}
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
// ApplicableMessage represents a message to be applied on the test vector.
|
||
|
type ApplicableMessage struct {
|
||
|
Epoch abi.ChainEpoch
|
||
|
Message *types.Message
|
||
|
Result *vm.ApplyRet
|
||
|
}
|
||
|
|
||
|
func (m *Messages) Sugar() *sugarMsg {
|
||
|
return &sugarMsg{m}
|
||
|
}
|
||
|
|
||
|
// All returns all ApplicableMessages that have been accumulated, in the same
|
||
|
// order they were added.
|
||
|
func (m *Messages) All() []*ApplicableMessage {
|
||
|
return m.messages
|
||
|
}
|
||
|
|
||
|
// Typed adds a typed call to this message accumulator.
|
||
|
func (m *Messages) Typed(from, to address.Address, typedm TypedCall, opts ...MsgOpt) *ApplicableMessage {
|
||
|
method, params := typedm()
|
||
|
return m.Raw(from, to, method, params, opts...)
|
||
|
}
|
||
|
|
||
|
// Raw adds a raw message to this message accumulator.
|
||
|
func (m *Messages) Raw(from, to address.Address, method abi.MethodNum, params []byte, opts ...MsgOpt) *ApplicableMessage {
|
||
|
options := m.defaults
|
||
|
for _, opt := range opts {
|
||
|
opt(&options)
|
||
|
}
|
||
|
|
||
|
msg := &types.Message{
|
||
|
To: to,
|
||
|
From: from,
|
||
|
Nonce: options.nonce,
|
||
|
Value: options.value,
|
||
|
Method: method,
|
||
|
Params: params,
|
||
|
GasPrice: options.gasPrice,
|
||
|
GasLimit: options.gasLimit,
|
||
|
}
|
||
|
|
||
|
am := &ApplicableMessage{
|
||
|
Epoch: options.epoch,
|
||
|
Message: msg,
|
||
|
}
|
||
|
|
||
|
m.messages = append(m.messages, am)
|
||
|
return am
|
||
|
}
|
||
|
|
||
|
// ApplyOne applies the provided message. The following constraints are checked:
|
||
|
// - all previous messages have been applied.
|
||
|
// - we know about this message (i.e. it has been added through Typed, Raw or Sugar).
|
||
|
func (m *Messages) ApplyOne(am *ApplicableMessage) {
|
||
|
var found bool
|
||
|
for _, other := range m.messages {
|
||
|
if am == other {
|
||
|
// we have scanned all preceding messages, and verified they had been applied.
|
||
|
// we are ready to perform the application.
|
||
|
found = true
|
||
|
break
|
||
|
}
|
||
|
// verify that preceding messages have been applied.
|
||
|
// this will abort if unsatisfied.
|
||
|
m.b.Assert.Nil(other.Result, "preceding messages must have been applied when calling Apply*; first unapplied: %v", other)
|
||
|
}
|
||
|
m.b.Assert.True(found, "ApplicableMessage not found")
|
||
|
m.b.applyMessage(am)
|
||
|
}
|
||
|
|
||
|
// ApplyN calls ApplyOne for the supplied messages, in the order they are passed.
|
||
|
// The constraints described in ApplyOne apply.
|
||
|
func (m *Messages) ApplyN(ams ...*ApplicableMessage) {
|
||
|
for _, am := range ams {
|
||
|
m.ApplyOne(am)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type msgOpts struct {
|
||
|
nonce uint64
|
||
|
value big.Int
|
||
|
gasPrice big.Int
|
||
|
gasLimit int64
|
||
|
epoch abi.ChainEpoch
|
||
|
}
|
||
|
|
||
|
// MsgOpt is an option configuring message value, gas parameters, execution
|
||
|
// epoch, and other elements.
|
||
|
type MsgOpt func(*msgOpts)
|
||
|
|
||
|
// Value sets a value on a message.
|
||
|
func Value(value big.Int) MsgOpt {
|
||
|
return func(opts *msgOpts) {
|
||
|
opts.value = value
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Nonce sets the nonce of a message.
|
||
|
func Nonce(n uint64) MsgOpt {
|
||
|
return func(opts *msgOpts) {
|
||
|
opts.nonce = n
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// GasLimit sets the gas limit of a message.
|
||
|
func GasLimit(limit int64) MsgOpt {
|
||
|
return func(opts *msgOpts) {
|
||
|
opts.gasLimit = limit
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// GasPrice sets the gas price of a message.
|
||
|
func GasPrice(price int64) MsgOpt {
|
||
|
return func(opts *msgOpts) {
|
||
|
opts.gasPrice = big.NewInt(price)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Epoch sets the epoch in which a message is to be executed.
|
||
|
func Epoch(epoch abi.ChainEpoch) MsgOpt {
|
||
|
return func(opts *msgOpts) {
|
||
|
opts.epoch = epoch
|
||
|
}
|
||
|
}
|