diff --git a/chain/datasource/datasource.go b/chain/datasource/datasource.go index 20f6df6a..e13d9ae3 100644 --- a/chain/datasource/datasource.go +++ b/chain/datasource/datasource.go @@ -31,6 +31,7 @@ import ( "github.com/filecoin-project/lily/chain/actors/adt/diff" "github.com/filecoin-project/lily/chain/actors/builtin/miner" "github.com/filecoin-project/lily/lens" + "github.com/filecoin-project/lily/lens/util" "github.com/filecoin-project/lily/metrics" "github.com/filecoin-project/lily/tasks" ) @@ -65,7 +66,7 @@ func init() { executedTsCacheSize = getCacheSizeFromEnv(executedTsCacheSizeEnv, 4) diffPreCommitCacheSize = getCacheSizeFromEnv(diffPreCommitCacheSizeEnv, 500) diffSectorCacheSize = getCacheSizeFromEnv(diffSectorCacheSizeEnv, 500) - actorCacheSize = getCacheSizeFromEnv(actorCacheSizeEnv, 1000) + actorCacheSize = getCacheSizeFromEnv(actorCacheSizeEnv, 5000) } var _ tasks.DataSource = (*DataSource)(nil) @@ -221,6 +222,55 @@ func (t *DataSource) Actor(ctx context.Context, addr address.Address, tsk types. return act, err } +// ActorInfo retrieves information about an actor at the given address within +// the context of a specific tipset. It first checks a cache for the requested +// information and returns it if available, otherwise it fetches the actor's +// details from the statetree. The retrieved actor information is cached +// for future access. If the actor information is successfully retrieved, +// it includes the actor's state and relevant metadata such as family and name. +// If the actor is not found or an error occurs during retrieval, the function +// returns an error. +func (t *DataSource) ActorInfo(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*tasks.ActorInfo, error) { + metrics.RecordInc(ctx, metrics.DataSourceActorCacheRead) + ctx, span := otel.Tracer("").Start(ctx, "DataSource.ActorInfo") + if span.IsRecording() { + span.SetAttributes(attribute.String("tipset", tsk.String())) + span.SetAttributes(attribute.String("address", addr.String())) + } + defer span.End() + + // Includes a prefix to prevent duplication of key names in the cache + key, keyErr := asKey(KeyPrefix{"ActorInfo"}, addr, tsk) + if keyErr == nil { + value, found := t.actorCache.Get(key) + if found { + metrics.RecordInc(ctx, metrics.DataSourceActorCacheHit) + return value.(*tasks.ActorInfo), nil + } + } + + act, err := t.Actor(ctx, addr, tsk) + actorInfo := tasks.ActorInfo{} + if err == nil { + if act.Address == nil { + act.Address = &addr + } + actorInfo.Actor = act + actorName, actorFamily, err := util.ActorNameAndFamilyFromCode(act.Code) + if err == nil { + actorInfo.ActorFamily = actorFamily + actorInfo.ActorName = actorName + } + } + + // Save the ActorInfo into cache + if err == nil && keyErr == nil { + t.actorCache.Add(key, &actorInfo) + } + + return &actorInfo, err +} + func (t *DataSource) MinerPower(ctx context.Context, addr address.Address, ts *types.TipSet) (*api.MinerPower, error) { ctx, span := otel.Tracer("").Start(ctx, "DataSource.MinerPower") if span.IsRecording() { @@ -528,3 +578,11 @@ func asKey(strs ...fmt.Stringer) (string, error) { } return sb.String(), nil } + +type KeyPrefix struct { + Prefix string +} + +func (k KeyPrefix) String() string { + return k.Prefix + ":" +} diff --git a/model/fevm/transaction.go b/model/fevm/transaction.go index 82ef76f6..c7c7db9a 100644 --- a/model/fevm/transaction.go +++ b/model/fevm/transaction.go @@ -49,6 +49,16 @@ type FEVMTransaction struct { R string `pg:",notnull"` // Transaction’s signature. Outputs of an ECDSA signature. S string `pg:",notnull"` + // Filecoin Address of the sender. + FromFilecoinAddress string `pg:",notnull"` + // Filecoin Address of the receiver. + ToFilecoinAddress string `pg:",notnull"` + // Human-readable identifier of sender (From). + FromActorName string `pg:",notnull"` + // Human-readable identifier of receiver (To). + ToActorName string `pg:",notnull"` + // On-chain message triggering the message. + MessageCid string `pg:",pk,notnull"` } func (f *FEVMTransaction) Persist(ctx context.Context, s model.StorageBatch, version model.Version) error { diff --git a/schemas/v1/31_add_message_and_address_and_actor_names_to_fevm_transcation.go b/schemas/v1/31_add_message_and_address_and_actor_names_to_fevm_transcation.go new file mode 100644 index 00000000..d5a71e6c --- /dev/null +++ b/schemas/v1/31_add_message_and_address_and_actor_names_to_fevm_transcation.go @@ -0,0 +1,27 @@ +package v1 + +func init() { + patches.Register( + 31, + ` + ALTER TABLE {{ .SchemaName | default "public"}}.fevm_transactions + ADD COLUMN IF NOT EXISTS "from_filecoin_address" text; + ALTER TABLE {{ .SchemaName | default "public"}}.fevm_transactions + ADD COLUMN IF NOT EXISTS "to_filecoin_address" text; + + ALTER TABLE {{ .SchemaName | default "public"}}.fevm_transactions + ADD COLUMN IF NOT EXISTS "from_actor_name" text; + ALTER TABLE {{ .SchemaName | default "public"}}.fevm_transactions + ADD COLUMN IF NOT EXISTS "to_actor_name" text; + + ALTER TABLE {{ .SchemaName | default "public"}}.fevm_transactions + ADD COLUMN IF NOT EXISTS "message_cid" text; + + COMMENT ON COLUMN {{ .SchemaName | default "public"}}.fevm_transactions.from_filecoin_address IS 'Filecoin Address of the sender.'; + COMMENT ON COLUMN {{ .SchemaName | default "public"}}.fevm_transactions.to_filecoin_address IS 'Filecoin Address of the receiver.'; + COMMENT ON COLUMN {{ .SchemaName | default "public"}}.fevm_transactions.from_actor_name IS 'Fully-versioned human-readable identifier of sender (From).'; + COMMENT ON COLUMN {{ .SchemaName | default "public"}}.fevm_transactions.to_actor_name IS 'Fully-versioned human-readable identifier of receiver (To).'; + COMMENT ON COLUMN {{ .SchemaName | default "public"}}.fevm_transactions.message_cid IS 'Filecoin Message Cid'; +`, + ) +} diff --git a/tasks/api.go b/tasks/api.go index 0c3134d6..61d2bdf7 100644 --- a/tasks/api.go +++ b/tasks/api.go @@ -48,10 +48,17 @@ type ActorStateChangeDiff map[address.Address]ActorStateChange type ActorStatesByType map[string][]*types.ActorV5 +type ActorInfo struct { + Actor *types.Actor + ActorName string + ActorFamily string +} + type DataSource interface { TipSet(ctx context.Context, tsk types.TipSetKey) (*types.TipSet, error) Actor(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*types.Actor, error) ActorState(ctx context.Context, addr address.Address, ts *types.TipSet) (*api.ActorState, error) + ActorInfo(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*ActorInfo, error) CirculatingSupply(ctx context.Context, ts *types.TipSet) (api.CirculatingSupply, error) MinerPower(ctx context.Context, addr address.Address, ts *types.TipSet) (*api.MinerPower, error) ActorStateChanges(ctx context.Context, ts, pts *types.TipSet) (ActorStateChangeDiff, error) diff --git a/tasks/fevm/transaction/tasks.go b/tasks/fevm/transaction/tasks.go index 72070143..d7504175 100644 --- a/tasks/fevm/transaction/tasks.go +++ b/tasks/fevm/transaction/tasks.go @@ -62,6 +62,11 @@ func (p *Task) ProcessTipSets(ctx context.Context, current *types.TipSet, execut if message.Message == nil { continue } + fromActorInfo, err := p.node.ActorInfo(ctx, message.Message.From, current.Key()) + if err != nil { + continue + } + if !util.IsEVMMessage(ctx, p.node, message.Message, current.Key()) { continue } @@ -99,6 +104,9 @@ func (p *Task) ProcessTipSets(ctx context.Context, current *types.TipSet, execut V: txn.V.String(), R: txn.R.String(), S: txn.S.String(), + FromFilecoinAddress: fromActorInfo.Actor.Address.String(), + FromActorName: fromActorInfo.ActorName, + MessageCid: message.Cid.String(), } if txn.BlockHash != nil { @@ -110,8 +118,17 @@ func (p *Task) ProcessTipSets(ctx context.Context, current *types.TipSet, execut if txn.TransactionIndex != nil { txnObj.TransactionIndex = uint64(*txn.TransactionIndex) } + + // Sometime the the "To" field could be nil if txn.To != nil { txnObj.To = txn.To.String() + + // Get the Actor from ActorInfo + toActorInfo, err := p.node.ActorInfo(ctx, message.Message.To, current.Key()) + if err == nil && toActorInfo.Actor != nil && toActorInfo.Actor.Address != nil { + txnObj.ToActorName = toActorInfo.ActorName + txnObj.ToFilecoinAddress = toActorInfo.Actor.Address.String() + } } if len(txn.AccessList) > 0 {