2019-07-12 10:43:15 +00:00
package types
import (
2019-08-21 17:15:28 +00:00
"bytes"
2019-07-12 10:43:15 +00:00
"fmt"
2020-05-14 19:28:33 +00:00
"github.com/filecoin-project/lotus/build"
2020-02-11 01:43:26 +00:00
"github.com/filecoin-project/specs-actors/actors/abi"
2020-05-14 19:28:33 +00:00
"github.com/filecoin-project/specs-actors/actors/abi/big"
2019-07-12 10:43:15 +00:00
block "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
"github.com/multiformats/go-multihash"
2020-05-14 19:28:33 +00:00
xerrors "golang.org/x/xerrors"
2019-07-12 10:43:15 +00:00
2019-12-19 20:13:17 +00:00
"github.com/filecoin-project/go-address"
2019-07-12 10:43:15 +00:00
)
2020-04-27 18:35:00 +00:00
const MessageVersion = 0
2020-03-25 19:13:09 +00:00
type ChainMsg interface {
Cid ( ) cid . Cid
VMMessage ( ) * Message
ToStorageBlock ( ) ( block . Block , error )
2020-06-26 23:13:14 +00:00
// FIXME: This is the *message* length, this name is misleading.
2020-03-25 19:13:09 +00:00
ChainLength ( ) int
}
2019-07-12 10:43:15 +00:00
type Message struct {
2020-04-27 18:35:00 +00:00
Version int64
2019-07-12 10:43:15 +00:00
To address . Address
From address . Address
Nonce uint64
Value BigInt
GasPrice BigInt
2020-03-18 20:45:37 +00:00
GasLimit int64
2019-07-12 10:43:15 +00:00
2020-02-11 01:43:26 +00:00
Method abi . MethodNum
2019-07-12 10:43:15 +00:00
Params [ ] byte
}
2020-06-02 14:29:39 +00:00
func ( m * Message ) Caller ( ) address . Address {
return m . From
2020-02-16 22:15:01 +00:00
}
2020-06-02 14:29:39 +00:00
func ( m * Message ) Receiver ( ) address . Address {
return m . To
2020-02-16 22:15:01 +00:00
}
2020-06-02 14:29:39 +00:00
func ( m * Message ) ValueReceived ( ) abi . TokenAmount {
return m . Value
2020-02-16 22:15:01 +00:00
}
2019-07-12 10:43:15 +00:00
func DecodeMessage ( b [ ] byte ) ( * Message , error ) {
var msg Message
2019-08-21 17:15:28 +00:00
if err := msg . UnmarshalCBOR ( bytes . NewReader ( b ) ) ; err != nil {
2019-07-12 10:43:15 +00:00
return nil , err
}
2020-04-27 18:35:00 +00:00
if msg . Version != MessageVersion {
return nil , fmt . Errorf ( "decoded message had incorrect version (%d)" , msg . Version )
}
2019-07-12 10:43:15 +00:00
return & msg , nil
}
func ( m * Message ) Serialize ( ) ( [ ] byte , error ) {
2019-08-21 17:15:28 +00:00
buf := new ( bytes . Buffer )
if err := m . MarshalCBOR ( buf ) ; err != nil {
return nil , err
}
return buf . Bytes ( ) , nil
2019-07-12 10:43:15 +00:00
}
2020-03-25 11:29:35 +00:00
func ( m * Message ) ChainLength ( ) int {
ser , err := m . Serialize ( )
if err != nil {
panic ( err )
}
return len ( ser )
}
2019-07-12 10:43:15 +00:00
func ( m * Message ) ToStorageBlock ( ) ( block . Block , error ) {
data , err := m . Serialize ( )
if err != nil {
return nil , err
}
2019-08-21 17:15:28 +00:00
pref := cid . NewPrefixV1 ( cid . DagCBOR , multihash . BLAKE2B_MIN + 31 )
2019-07-12 10:43:15 +00:00
c , err := pref . Sum ( data )
if err != nil {
return nil , err
}
return block . NewBlockWithCid ( data , c )
}
2019-08-01 20:40:47 +00:00
func ( m * Message ) Cid ( ) cid . Cid {
b , err := m . ToStorageBlock ( )
if err != nil {
2019-11-16 23:41:14 +00:00
panic ( fmt . Sprintf ( "failed to marshal message: %s" , err ) ) // I think this is maybe sketchy, what happens if we try to serialize a message with an undefined address in it?
2019-08-01 20:40:47 +00:00
}
return b . Cid ( )
}
2019-09-26 03:48:53 +00:00
func ( m * Message ) RequiredFunds ( ) BigInt {
return BigAdd (
m . Value ,
2020-03-18 20:45:37 +00:00
BigMul ( m . GasPrice , NewInt ( uint64 ( m . GasLimit ) ) ) ,
2019-09-26 03:48:53 +00:00
)
}
2019-09-27 23:55:15 +00:00
func ( m * Message ) VMMessage ( ) * Message {
return m
}
2019-11-19 21:27:25 +00:00
func ( m * Message ) Equals ( o * Message ) bool {
return m . Cid ( ) == o . Cid ( )
}
2020-05-14 19:28:33 +00:00
func ( m * Message ) ValidForBlockInclusion ( minGas int64 ) error {
if m . Version != 0 {
return xerrors . New ( "'Version' unsupported" )
}
if m . To == address . Undef {
return xerrors . New ( "'To' address cannot be empty" )
}
if m . From == address . Undef {
return xerrors . New ( "'From' address cannot be empty" )
}
if m . Value . LessThan ( big . Zero ( ) ) {
return xerrors . New ( "'Value' field cannot be negative" )
}
if m . Value . GreaterThan ( TotalFilecoinInt ) {
return xerrors . New ( "'Value' field cannot be greater than total filecoin supply" )
}
if m . GasPrice . LessThan ( big . Zero ( ) ) {
return xerrors . New ( "'GasPrice' field cannot be negative" )
}
if m . GasLimit > build . BlockGasLimit {
return xerrors . New ( "'GasLimit' field cannot be greater than a block's gas limit" )
}
// since prices might vary with time, this is technically semantic validation
if m . GasLimit < minGas {
return xerrors . New ( "'GasLimit' field cannot be less than the cost of storing a message on chain" )
}
return nil
}