diff --git a/.gitignore b/.gitignore index af6fbf08..3da9bd3a 100644 --- a/.gitignore +++ b/.gitignore @@ -152,3 +152,5 @@ fabric.properties # Ignore all local history of files .history .ionide + +cmd/tracedl/tracedl \ No newline at end of file diff --git a/cmd/tracedl/README.md b/cmd/tracedl/README.md index 750a5873..b3baf09d 100644 --- a/cmd/tracedl/README.md +++ b/cmd/tracedl/README.md @@ -3,8 +3,8 @@ Helper tool to download raw logs from a node for testing purposes. ### Usage -Replace values in `config.yaml` with desired values. +Replace values in `config.yaml` with desired values. ``` go build @@ -18,12 +18,20 @@ go build Download native logs and store as gzip. -`./tracedl get --type nativelog --compress gz --height 3897964` +`./tracedl get --type nativelog --compress gz --height 3897964 --outPath ../../data/heights` Download eth logs and store as gzip. -`./tracedl get --type ethlog --compress gz --height 3897964` +`./tracedl get --type ethlog --compress gz --height 3897964 --outPath ../../data/heights` Download tipset and store as gzip. -`./tracedl get --type tipset --compress gz --height 3897964` \ No newline at end of file +`./tracedl get --type tipset --compress gz --height 3897964 --outPath ../../data/heights` + +--- +You can use the `script.sh` to automate the download of traces, native logs, eth logs, and tipsets for specified heights. + +1. Open `script.sh` and modify the heights array with the desired heights. +2. Run the script: + +`./script.sh` diff --git a/cmd/tracedl/config.yaml b/cmd/tracedl/config.yaml index 8a728713..97e7d0a7 100644 --- a/cmd/tracedl/config.yaml +++ b/cmd/tracedl/config.yaml @@ -1,4 +1,4 @@ - network_name: "mainnet" + network_name: "calibration" symbol: "fil" - node_url: "wss://node-fil-mainnet-next.zondax.ch/rpc/v1" + node_url: "wss://node-fil-calibration-next.zondax.ch/rpc/v1" node_token: "" diff --git a/cmd/tracedl/script.sh b/cmd/tracedl/script.sh new file mode 100755 index 00000000..e36419e9 --- /dev/null +++ b/cmd/tracedl/script.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Heights to download traces, native logs, eth logs, and tipsets +heights=(1698055 1576593 1572087 1552242 1352134 1334035 1289201 1258459 1256171) + +# Function to download files +download_files() { + local height=$1 + ./tracedl get --type traces --compress gz --height "$height" --outPath ../../data/heights + ./tracedl get --type nativelog --compress gz --height "$height" --outPath ../../data/heights + ./tracedl get --type ethlog --compress gz --height "$height" --outPath ../../data/heights + ./tracedl get --type tipset --compress gz --height "$height" --outPath ../../data/heights +} + +# Download files for each height +for height in "${heights[@]}"; do + download_files "$height" +done diff --git a/data/heights/ethlog_1162295.json.gz b/data/heights/ethlog_1162295.json.gz new file mode 100755 index 00000000..714b70e5 Binary files /dev/null and b/data/heights/ethlog_1162295.json.gz differ diff --git a/data/heights/ethlog_1256171.json.gz b/data/heights/ethlog_1256171.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_1256171.json.gz differ diff --git a/data/heights/ethlog_1258459.json.gz b/data/heights/ethlog_1258459.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_1258459.json.gz differ diff --git a/data/heights/ethlog_1289201.json.gz b/data/heights/ethlog_1289201.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_1289201.json.gz differ diff --git a/data/heights/ethlog_1334035.json.gz b/data/heights/ethlog_1334035.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_1334035.json.gz differ diff --git a/data/heights/ethlog_1352134.json.gz b/data/heights/ethlog_1352134.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_1352134.json.gz differ diff --git a/data/heights/ethlog_14107.json.gz b/data/heights/ethlog_14107.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_14107.json.gz differ diff --git a/data/heights/ethlog_1467665.json.gz b/data/heights/ethlog_1467665.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_1467665.json.gz differ diff --git a/data/heights/ethlog_1552242.json.gz b/data/heights/ethlog_1552242.json.gz new file mode 100755 index 00000000..584aa9ea Binary files /dev/null and b/data/heights/ethlog_1552242.json.gz differ diff --git a/data/heights/ethlog_1572087.json.gz b/data/heights/ethlog_1572087.json.gz new file mode 100755 index 00000000..42a93c39 Binary files /dev/null and b/data/heights/ethlog_1572087.json.gz differ diff --git a/data/heights/ethlog_1576593.json.gz b/data/heights/ethlog_1576593.json.gz new file mode 100755 index 00000000..70718ef6 Binary files /dev/null and b/data/heights/ethlog_1576593.json.gz differ diff --git a/data/heights/ethlog_1698055.json.gz b/data/heights/ethlog_1698055.json.gz new file mode 100755 index 00000000..6e56a125 Binary files /dev/null and b/data/heights/ethlog_1698055.json.gz differ diff --git a/data/heights/ethlog_197673.json.gz b/data/heights/ethlog_197673.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_197673.json.gz differ diff --git a/data/heights/ethlog_38895.json.gz b/data/heights/ethlog_38895.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_38895.json.gz differ diff --git a/data/heights/ethlog_38940.json.gz b/data/heights/ethlog_38940.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_38940.json.gz differ diff --git a/data/heights/ethlog_39035.json.gz b/data/heights/ethlog_39035.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_39035.json.gz differ diff --git a/data/heights/ethlog_47635.json.gz b/data/heights/ethlog_47635.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_47635.json.gz differ diff --git a/data/heights/ethlog_47645.json.gz b/data/heights/ethlog_47645.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_47645.json.gz differ diff --git a/data/heights/ethlog_78689.json.gz b/data/heights/ethlog_78689.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/ethlog_78689.json.gz differ diff --git a/data/heights/nativelog_1162295.json.gz b/data/heights/nativelog_1162295.json.gz new file mode 100755 index 00000000..062a9166 Binary files /dev/null and b/data/heights/nativelog_1162295.json.gz differ diff --git a/data/heights/nativelog_1256171.json.gz b/data/heights/nativelog_1256171.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_1256171.json.gz differ diff --git a/data/heights/nativelog_1258459.json.gz b/data/heights/nativelog_1258459.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_1258459.json.gz differ diff --git a/data/heights/nativelog_1289201.json.gz b/data/heights/nativelog_1289201.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_1289201.json.gz differ diff --git a/data/heights/nativelog_1334035.json.gz b/data/heights/nativelog_1334035.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_1334035.json.gz differ diff --git a/data/heights/nativelog_1352134.json.gz b/data/heights/nativelog_1352134.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_1352134.json.gz differ diff --git a/data/heights/nativelog_14107.json.gz b/data/heights/nativelog_14107.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_14107.json.gz differ diff --git a/data/heights/nativelog_1467665.json.gz b/data/heights/nativelog_1467665.json.gz new file mode 100755 index 00000000..a0957769 Binary files /dev/null and b/data/heights/nativelog_1467665.json.gz differ diff --git a/data/heights/nativelog_1552242.json.gz b/data/heights/nativelog_1552242.json.gz new file mode 100755 index 00000000..9a7f89fc Binary files /dev/null and b/data/heights/nativelog_1552242.json.gz differ diff --git a/data/heights/nativelog_1572087.json.gz b/data/heights/nativelog_1572087.json.gz new file mode 100755 index 00000000..90d871e4 Binary files /dev/null and b/data/heights/nativelog_1572087.json.gz differ diff --git a/data/heights/nativelog_1576593.json.gz b/data/heights/nativelog_1576593.json.gz new file mode 100755 index 00000000..01ff687a Binary files /dev/null and b/data/heights/nativelog_1576593.json.gz differ diff --git a/data/heights/nativelog_1698055.json.gz b/data/heights/nativelog_1698055.json.gz new file mode 100755 index 00000000..468a871a Binary files /dev/null and b/data/heights/nativelog_1698055.json.gz differ diff --git a/data/heights/nativelog_197673.json.gz b/data/heights/nativelog_197673.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_197673.json.gz differ diff --git a/data/heights/nativelog_38895.json.gz b/data/heights/nativelog_38895.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_38895.json.gz differ diff --git a/data/heights/nativelog_38940.json.gz b/data/heights/nativelog_38940.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_38940.json.gz differ diff --git a/data/heights/nativelog_39035.json.gz b/data/heights/nativelog_39035.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_39035.json.gz differ diff --git a/data/heights/nativelog_47635.json.gz b/data/heights/nativelog_47635.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_47635.json.gz differ diff --git a/data/heights/nativelog_47645.json.gz b/data/heights/nativelog_47645.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_47645.json.gz differ diff --git a/data/heights/nativelog_78689.json.gz b/data/heights/nativelog_78689.json.gz new file mode 100755 index 00000000..f4d1c8cf Binary files /dev/null and b/data/heights/nativelog_78689.json.gz differ diff --git a/data/heights/tipset_1162295.json.gz b/data/heights/tipset_1162295.json.gz new file mode 100755 index 00000000..87f8966a Binary files /dev/null and b/data/heights/tipset_1162295.json.gz differ diff --git a/data/heights/tipset_1256171.json.gz b/data/heights/tipset_1256171.json.gz new file mode 100755 index 00000000..af4a3ecf Binary files /dev/null and b/data/heights/tipset_1256171.json.gz differ diff --git a/data/heights/tipset_1258459.json.gz b/data/heights/tipset_1258459.json.gz new file mode 100755 index 00000000..38c7285e Binary files /dev/null and b/data/heights/tipset_1258459.json.gz differ diff --git a/data/heights/tipset_1289201.json.gz b/data/heights/tipset_1289201.json.gz new file mode 100755 index 00000000..3825a26d Binary files /dev/null and b/data/heights/tipset_1289201.json.gz differ diff --git a/data/heights/tipset_1334035.json.gz b/data/heights/tipset_1334035.json.gz new file mode 100755 index 00000000..7595a146 Binary files /dev/null and b/data/heights/tipset_1334035.json.gz differ diff --git a/data/heights/tipset_1352134.json.gz b/data/heights/tipset_1352134.json.gz new file mode 100755 index 00000000..b85bbd24 Binary files /dev/null and b/data/heights/tipset_1352134.json.gz differ diff --git a/data/heights/tipset_14107.json.gz b/data/heights/tipset_14107.json.gz new file mode 100755 index 00000000..ef072630 Binary files /dev/null and b/data/heights/tipset_14107.json.gz differ diff --git a/data/heights/tipset_1467665.json.gz b/data/heights/tipset_1467665.json.gz new file mode 100755 index 00000000..6873b683 Binary files /dev/null and b/data/heights/tipset_1467665.json.gz differ diff --git a/data/heights/tipset_1552242.json.gz b/data/heights/tipset_1552242.json.gz new file mode 100755 index 00000000..80280cc8 Binary files /dev/null and b/data/heights/tipset_1552242.json.gz differ diff --git a/data/heights/tipset_1572087.json.gz b/data/heights/tipset_1572087.json.gz new file mode 100755 index 00000000..b4994eff Binary files /dev/null and b/data/heights/tipset_1572087.json.gz differ diff --git a/data/heights/tipset_1576593.json.gz b/data/heights/tipset_1576593.json.gz new file mode 100755 index 00000000..eda9883d Binary files /dev/null and b/data/heights/tipset_1576593.json.gz differ diff --git a/data/heights/tipset_1698055.json.gz b/data/heights/tipset_1698055.json.gz new file mode 100755 index 00000000..07fd4f39 Binary files /dev/null and b/data/heights/tipset_1698055.json.gz differ diff --git a/data/heights/tipset_197673.json.gz b/data/heights/tipset_197673.json.gz new file mode 100755 index 00000000..1d5b0dde Binary files /dev/null and b/data/heights/tipset_197673.json.gz differ diff --git a/data/heights/tipset_38895.json.gz b/data/heights/tipset_38895.json.gz new file mode 100755 index 00000000..c1435369 Binary files /dev/null and b/data/heights/tipset_38895.json.gz differ diff --git a/data/heights/tipset_38940.json.gz b/data/heights/tipset_38940.json.gz new file mode 100755 index 00000000..6675c80b Binary files /dev/null and b/data/heights/tipset_38940.json.gz differ diff --git a/data/heights/tipset_39035.json.gz b/data/heights/tipset_39035.json.gz new file mode 100755 index 00000000..6c303ee2 Binary files /dev/null and b/data/heights/tipset_39035.json.gz differ diff --git a/data/heights/tipset_47635.json.gz b/data/heights/tipset_47635.json.gz new file mode 100755 index 00000000..b349d32d Binary files /dev/null and b/data/heights/tipset_47635.json.gz differ diff --git a/data/heights/tipset_47645.json.gz b/data/heights/tipset_47645.json.gz new file mode 100755 index 00000000..b3d8eeef Binary files /dev/null and b/data/heights/tipset_47645.json.gz differ diff --git a/data/heights/tipset_78689.json.gz b/data/heights/tipset_78689.json.gz new file mode 100755 index 00000000..a0598270 Binary files /dev/null and b/data/heights/tipset_78689.json.gz differ diff --git a/data/heights/traces_1162295.json.gz b/data/heights/traces_1162295.json.gz new file mode 100755 index 00000000..5489e0b9 Binary files /dev/null and b/data/heights/traces_1162295.json.gz differ diff --git a/data/heights/traces_1256171.json.gz b/data/heights/traces_1256171.json.gz new file mode 100755 index 00000000..f1b979ec Binary files /dev/null and b/data/heights/traces_1256171.json.gz differ diff --git a/data/heights/traces_1258459.json.gz b/data/heights/traces_1258459.json.gz new file mode 100755 index 00000000..47f08d41 Binary files /dev/null and b/data/heights/traces_1258459.json.gz differ diff --git a/data/heights/traces_1289201.json.gz b/data/heights/traces_1289201.json.gz new file mode 100755 index 00000000..57df0d22 Binary files /dev/null and b/data/heights/traces_1289201.json.gz differ diff --git a/data/heights/traces_1334035.json.gz b/data/heights/traces_1334035.json.gz new file mode 100755 index 00000000..3c8350f9 Binary files /dev/null and b/data/heights/traces_1334035.json.gz differ diff --git a/data/heights/traces_1352134.json.gz b/data/heights/traces_1352134.json.gz new file mode 100755 index 00000000..7e93356c Binary files /dev/null and b/data/heights/traces_1352134.json.gz differ diff --git a/data/heights/traces_14107.json.gz b/data/heights/traces_14107.json.gz new file mode 100755 index 00000000..e7bd129d Binary files /dev/null and b/data/heights/traces_14107.json.gz differ diff --git a/data/heights/traces_1467665.json.gz b/data/heights/traces_1467665.json.gz new file mode 100755 index 00000000..29fabab9 Binary files /dev/null and b/data/heights/traces_1467665.json.gz differ diff --git a/data/heights/traces_1552242.json.gz b/data/heights/traces_1552242.json.gz new file mode 100755 index 00000000..f9a67de9 Binary files /dev/null and b/data/heights/traces_1552242.json.gz differ diff --git a/data/heights/traces_1572087.json.gz b/data/heights/traces_1572087.json.gz new file mode 100755 index 00000000..337d6c93 Binary files /dev/null and b/data/heights/traces_1572087.json.gz differ diff --git a/data/heights/traces_1576593.json.gz b/data/heights/traces_1576593.json.gz new file mode 100755 index 00000000..36c80672 Binary files /dev/null and b/data/heights/traces_1576593.json.gz differ diff --git a/data/heights/traces_1698055.json.gz b/data/heights/traces_1698055.json.gz new file mode 100755 index 00000000..72e75b2a Binary files /dev/null and b/data/heights/traces_1698055.json.gz differ diff --git a/data/heights/traces_197673.json.gz b/data/heights/traces_197673.json.gz new file mode 100755 index 00000000..fa6e68cf Binary files /dev/null and b/data/heights/traces_197673.json.gz differ diff --git a/data/heights/traces_38895.json.gz b/data/heights/traces_38895.json.gz new file mode 100755 index 00000000..7792a5d4 Binary files /dev/null and b/data/heights/traces_38895.json.gz differ diff --git a/data/heights/traces_38940.json.gz b/data/heights/traces_38940.json.gz new file mode 100755 index 00000000..74d0e27d Binary files /dev/null and b/data/heights/traces_38940.json.gz differ diff --git a/data/heights/traces_39035.json.gz b/data/heights/traces_39035.json.gz new file mode 100755 index 00000000..7378bd53 Binary files /dev/null and b/data/heights/traces_39035.json.gz differ diff --git a/data/heights/traces_47635.json.gz b/data/heights/traces_47635.json.gz new file mode 100755 index 00000000..1e2dc535 Binary files /dev/null and b/data/heights/traces_47635.json.gz differ diff --git a/data/heights/traces_47645.json.gz b/data/heights/traces_47645.json.gz new file mode 100755 index 00000000..114c5468 Binary files /dev/null and b/data/heights/traces_47645.json.gz differ diff --git a/data/heights/traces_78689.json.gz b/data/heights/traces_78689.json.gz new file mode 100755 index 00000000..f83dda47 Binary files /dev/null and b/data/heights/traces_78689.json.gz differ diff --git a/factory.go b/factory.go index bd2792ea..044b6573 100644 --- a/factory.go +++ b/factory.go @@ -8,6 +8,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/manifest" types2 "github.com/filecoin-project/lotus/chain/types" "github.com/google/uuid" "github.com/zondax/fil-parser/actors/cache" @@ -40,6 +41,7 @@ type Parser interface { NodeVersionsSupported() []string ParseTransactions(ctx context.Context, txsData types.TxsData) (*types.TxsParsedResult, error) ParseNativeEvents(ctx context.Context, eventsData types.EventsData) (*types.EventsParsedResult, error) + ParseMultisigEvents(ctx context.Context, multisigTxs []*types.Transaction, tipsetCid string, tipsetKey types2.TipSetKey) (*types.MultisigEvents, error) ParseEthLogs(ctx context.Context, eventsData types.EventsData) (*types.EventsParsedResult, error) GetBaseFee(traces []byte, tipset *types.ExtendedTipSet) (uint64, error) IsNodeVersionSupported(ver string) bool @@ -141,6 +143,14 @@ func (p *FilecoinParser) ParseEthLogs(ctx context.Context, eventsData types.Even return parsedResult, nil } +func (p *FilecoinParser) ParseMultisigEvents(ctx context.Context, txs []*types.Transaction, tipsetCid string, tipsetKey types2.TipSetKey) (*types.MultisigEvents, error) { + multisigTxs, err := p.Helper.FilterTxsByActorType(ctx, txs, manifest.MultisigKey, tipsetKey) + if err != nil { + return nil, err + } + return p.parserV2.ParseMultisigEvents(ctx, multisigTxs, tipsetCid, tipsetKey) +} + func (p *FilecoinParser) translateParserVersionFromMetadata(metadata types.BlockMetadata) (string, error) { switch { // The empty string is for backwards compatibility with older traces versions diff --git a/go.mod b/go.mod index a731c80f..397717dd 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/orcaman/concurrent-map v1.0.0 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.9.0 - github.com/whyrusleeping/cbor-gen v0.1.1 + github.com/whyrusleeping/cbor-gen v0.1.2 github.com/zondax/golem v0.14.1 github.com/zondax/rosetta-filecoin-lib v1.2603.0 github.com/zondax/znats v0.1.1 @@ -60,7 +60,7 @@ require ( github.com/filecoin-project/go-hamt-ipld v0.1.5 // indirect github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0 // indirect github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0 // indirect - github.com/filecoin-project/go-jsonrpc v0.4.0 + github.com/filecoin-project/go-jsonrpc v0.5.0 github.com/filecoin-project/go-padreader v0.0.1 // indirect github.com/filecoin-project/go-statemachine v1.0.3 // indirect github.com/filecoin-project/go-statestore v0.2.0 // indirect @@ -121,7 +121,7 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/jpillora/backoff v1.0.0 // indirect - github.com/klauspost/compress v1.17.8 + github.com/klauspost/compress v1.17.9 github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect @@ -149,7 +149,7 @@ require ( github.com/multiformats/go-multiaddr v0.12.3 // indirect github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect github.com/multiformats/go-multibase v0.2.0 // indirect - github.com/multiformats/go-multicodec v0.9.0 + github.com/multiformats/go-multicodec v0.9.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect github.com/multiformats/go-multistream v0.5.0 // indirect github.com/multiformats/go-varint v0.0.7 // indirect diff --git a/go.sum b/go.sum index 504c271f..82af7438 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,8 @@ github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0/go.mod h1:7aWZdaQ1b16BVoQUYR+ github.com/filecoin-project/go-hamt-ipld/v3 v3.0.1/go.mod h1:gXpNmr3oQx8l3o7qkGyDjJjYSRX7hp/FGOStdqrWyDI= github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0 h1:rVVNq0x6RGQIzCo1iiJlGFm9AGIZzeifggxtKMU7zmI= github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0/go.mod h1:bxmzgT8tmeVQA1/gvBwFmYdT8SOFUwB3ovSUfG1Ux0g= -github.com/filecoin-project/go-jsonrpc v0.4.0 h1:W6kEt8YCQGUZpmvDOvLF+vKwRhrLPBN+kaSHYLEio+c= -github.com/filecoin-project/go-jsonrpc v0.4.0/go.mod h1:/n/niXcS4ZQua6i37LcVbY1TmlJR0UIK9mDFQq2ICek= +github.com/filecoin-project/go-jsonrpc v0.5.0 h1:6PZghgMaM9wSjlhxkDD+YgZ+oucBUIkJOfVc7SdQBTE= +github.com/filecoin-project/go-jsonrpc v0.5.0/go.mod h1:/n/niXcS4ZQua6i37LcVbY1TmlJR0UIK9mDFQq2ICek= github.com/filecoin-project/go-padreader v0.0.1 h1:8h2tVy5HpoNbr2gBRr+WD6zV6VD6XHig+ynSGJg8ZOs= github.com/filecoin-project/go-padreader v0.0.1/go.mod h1:VYVPJqwpsfmtoHnAmPx6MUwmrK6HIcDqZJiuZhtmfLQ= github.com/filecoin-project/go-state-types v0.0.0-20200903145444-247639ffa6ad/go.mod h1:IQ0MBPnonv35CJHtWSN3YY1Hz2gkPru1Q9qoaYLxx9I= @@ -472,8 +472,8 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= @@ -834,8 +834,8 @@ github.com/whyrusleeping/cbor-gen v0.0.0-20200806213330-63aa96ca5488/go.mod h1:f github.com/whyrusleeping/cbor-gen v0.0.0-20200810223238-211df3b9e24c/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/cbor-gen v0.0.0-20200812213548-958ddffe352c/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= github.com/whyrusleeping/cbor-gen v0.0.0-20210118024343-169e9d70c0c2/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= -github.com/whyrusleeping/cbor-gen v0.1.1 h1:eKfcJIoxivjMtwfCfmJAqSF56MHcWqyIScXwaC1VBgw= -github.com/whyrusleeping/cbor-gen v0.1.1/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= +github.com/whyrusleeping/cbor-gen v0.1.2 h1:WQFlrPhpcQl+M2/3dP5cvlTLWPVsL6LGBb9jJt6l/cA= +github.com/whyrusleeping/cbor-gen v0.1.2/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= diff --git a/parser/constants.go b/parser/constants.go index 5cee0ef2..f7d1d91c 100644 --- a/parser/constants.go +++ b/parser/constants.go @@ -177,6 +177,8 @@ const ( MethodBurnFromExported = "BurnFromExported" // MethodsDatacap MethodAllowanceExported = "AllowanceExported" // MethodsDatacap MethodGranularityExported = "GranularityExported" // MethodsDatacap + + MethodUnknown = "Unknown" // Common ) // SupportedOperations operations that will be parsed diff --git a/parser/helper/helpers.go b/parser/helper/helpers.go index 4d90a1e8..3c7719c3 100644 --- a/parser/helper/helpers.go +++ b/parser/helper/helpers.go @@ -3,6 +3,7 @@ package helper import ( "context" "errors" + "strings" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" @@ -172,3 +173,45 @@ func (h *Helper) GetMethodName(msg *parser.LotusMessage, height int64, key filTy func (h *Helper) GetEVMSelectorSig(ctx context.Context, selectorID string) (string, error) { return h.actorCache.GetEVMSelectorSig(ctx, selectorID) } + +func (h *Helper) FilterTxsByActorType(ctx context.Context, txs []*types.Transaction, actorType string, tipsetKey filTypes.TipSetKey) ([]*types.Transaction, error) { + var result []*types.Transaction + for _, tx := range txs { + addrTo, err := address.NewFromString(tx.TxTo) + if err != nil { + h.logger.Sugar().Errorf("could not parse address. Err: %s", err) + continue + } + addrFrom, err := address.NewFromString(tx.TxFrom) + if err != nil { + h.logger.Sugar().Errorf("could not parse address. Err: %s", err) + continue + } + + isType, err := h.isAnyAddressOfType(ctx, []address.Address{addrTo, addrFrom}, int64(tx.Height), tipsetKey, actorType) + if err != nil { + h.logger.Sugar().Errorf("could not get actor type from address. Err: %s", err) + continue + } + if !isType { + continue + } + + result = append(result, tx) + } + + return result, nil +} + +func (h *Helper) isAnyAddressOfType(_ context.Context, addresses []address.Address, height int64, key filTypes.TipSetKey, actorType string) (bool, error) { + for _, addr := range addresses { + actorName, err := h.GetActorNameFromAddress(addr, height, key) + if err != nil { + return false, err + } + if strings.EqualFold(actorName, actorType) { + return true, nil + } + } + return false, nil +} diff --git a/parser/v1/parser.go b/parser/v1/parser.go index 23d4b4e0..b19474f3 100644 --- a/parser/v1/parser.go +++ b/parser/v1/parser.go @@ -17,6 +17,7 @@ import ( "github.com/zondax/fil-parser/parser/helper" typesV1 "github.com/zondax/fil-parser/parser/v1/types" "github.com/zondax/fil-parser/tools" + multisigTools "github.com/zondax/fil-parser/tools/multisig" "github.com/zondax/fil-parser/types" "go.uber.org/zap" ) @@ -26,19 +27,21 @@ const Version = "v1" var NodeVersionsSupported = []string{"v1.21", "v1.22"} type Parser struct { - actorParser *actors.ActorParser - addresses *types.AddressInfoMap - txCidEquivalents []types.TxCidTranslation - helper *helper.Helper - logger *zap.Logger + actorParser *actors.ActorParser + addresses *types.AddressInfoMap + txCidEquivalents []types.TxCidTranslation + helper *helper.Helper + logger *zap.Logger + multisigEventGenerator multisigTools.EventGenerator } func NewParser(helper *helper.Helper, logger *zap.Logger) *Parser { return &Parser{ - actorParser: actors.NewActorParser(helper, logger), - addresses: types.NewAddressInfoMap(), - helper: helper, - logger: logger2.GetSafeLogger(logger), + actorParser: actors.NewActorParser(helper, logger), + addresses: types.NewAddressInfoMap(), + helper: helper, + logger: logger2.GetSafeLogger(logger), + multisigEventGenerator: multisigTools.NewEventGenerator(helper, logger2.GetSafeLogger(logger)), } } @@ -165,6 +168,10 @@ func (p *Parser) ParseTransactions(ctx context.Context, txsData types.TxsData) ( }, nil } +func (p *Parser) ParseMultisigEvents(ctx context.Context, multisigTxs []*types.Transaction, tipsetCid string, tipsetKey filTypes.TipSetKey) (*types.MultisigEvents, error) { + return nil, errors.New("unimplimented") +} + func (p *Parser) ParseNativeEvents(_ context.Context, _ types.EventsData) (*types.EventsParsedResult, error) { return nil, errors.New("unimplimented") } diff --git a/parser/v2/parser.go b/parser/v2/parser.go index 1155666b..c64890a6 100644 --- a/parser/v2/parser.go +++ b/parser/v2/parser.go @@ -16,6 +16,7 @@ import ( typesV2 "github.com/zondax/fil-parser/parser/v2/types" "github.com/zondax/fil-parser/tools" eventTools "github.com/zondax/fil-parser/tools/events" + multisigTools "github.com/zondax/fil-parser/tools/multisig" "github.com/zondax/fil-parser/types" "github.com/bytedance/sonic" @@ -30,19 +31,21 @@ const Version = "v2" var NodeVersionsSupported = []string{"v1.23", "v1.24", "v1.25", "v1.26"} type Parser struct { - actorParser *actors.ActorParser - addresses *types.AddressInfoMap - txCidEquivalents []types.TxCidTranslation - helper *helper.Helper - logger *zap.Logger + actorParser *actors.ActorParser + addresses *types.AddressInfoMap + txCidEquivalents []types.TxCidTranslation + helper *helper.Helper + logger *zap.Logger + multisigEventGenerator multisigTools.EventGenerator } func NewParser(helper *helper.Helper, logger *zap.Logger) *Parser { return &Parser{ - actorParser: actors.NewActorParser(helper, logger), - addresses: types.NewAddressInfoMap(), - helper: helper, - logger: logger2.GetSafeLogger(logger), + actorParser: actors.NewActorParser(helper, logger), + addresses: types.NewAddressInfoMap(), + helper: helper, + logger: logger2.GetSafeLogger(logger), + multisigEventGenerator: multisigTools.NewEventGenerator(helper, logger2.GetSafeLogger(logger)), } } @@ -179,6 +182,10 @@ func (p *Parser) ParseEthLogs(_ context.Context, eventsData types.EventsData) (* return &types.EventsParsedResult{EVMEvents: len(parsed), ParsedEvents: parsed}, nil } +func (p *Parser) ParseMultisigEvents(ctx context.Context, multisigTxs []*types.Transaction, tipsetCid string, tipsetKey filTypes.TipSetKey) (*types.MultisigEvents, error) { + return p.multisigEventGenerator.GenerateMultisigEvents(ctx, multisigTxs, tipsetCid, tipsetKey) +} + func (p *Parser) GetBaseFee(traces []byte, tipset *types.ExtendedTipSet) (uint64, error) { // Unmarshal into vComputeState computeState := &typesV2.ComputeStateOutputV2{} diff --git a/parser_test.go b/parser_test.go index b8f38187..1b7d88bc 100644 --- a/parser_test.go +++ b/parser_test.go @@ -7,6 +7,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "math" "math/big" "math/rand" "net/http" @@ -18,15 +19,18 @@ import ( "github.com/ipfs/go-cid" "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/codec/dagcbor" + "github.com/ipld/go-ipld-prime/datamodel" "github.com/ipld/go-ipld-prime/node/basicnode" "github.com/stretchr/testify/assert" "go.uber.org/zap" "github.com/filecoin-project/go-address" + filBig "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/lotus/api" filTypes "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" + cidLink "github.com/ipld/go-ipld-prime/linking/cid" "github.com/zondax/fil-parser/actors/cache/impl/common" v1 "github.com/zondax/fil-parser/parser/v1" v2 "github.com/zondax/fil-parser/parser/v2" @@ -628,6 +632,30 @@ func TestParser_ParseEvents_FVM_FromTraceFile(t *testing.T) { } } +func buildCidLink(cid cid.Cid) datamodel.Link { + return cidLink.Link{Cid: cid} +} + +func ipldEncode(t *testing.T, builder datamodel.NodeBuilder, data any) []byte { + var err error + + switch x := data.(type) { + case string: + err = builder.AssignString(x) + case []byte: + err = builder.AssignBytes(x) + case datamodel.Link: + err = builder.AssignLink(x) + case int64: + err = builder.AssignInt(x) + } + + require.NoError(t, err) + encoded, err := ipld.Encode(builder.Build(), dagcbor.Encode) + require.NoError(t, err) + return encoded +} + func TestParser_ParseNativeEvents_FVM(t *testing.T) { // we need any random number for the test //nolint:gosec @@ -645,17 +673,43 @@ func TestParser_ParseNativeEvents_FVM(t *testing.T) { parser, err := NewFilecoinParser(nil, getCacheDataSource(t, calibNextNodeUrl), logger) require.NoError(t, err) - ipldNodeBuilder := basicnode.Prototype.String.NewBuilder() - err = ipldNodeBuilder.AssignString("market_deals_event") - assert.NoError(t, err) - eventType, err := ipld.Encode(ipldNodeBuilder.Build(), dagcbor.Encode) - assert.NoError(t, err) + eventType := ipldEncode(t, basicnode.Prototype.String.NewBuilder(), "market_deals_event") + eventData := ipldEncode(t, basicnode.Prototype.Bytes.NewBuilder(), []byte("test_data")) - ipldNodeBuilder = basicnode.Prototype.Bytes.NewBuilder() - err = ipldNodeBuilder.AssignBytes([]byte("test data")) - assert.NoError(t, err) - eventData, err := ipld.Encode(ipldNodeBuilder.Build(), dagcbor.Encode) - assert.NoError(t, err) + // cid event data + eventCid, err := cid.Decode("baga6ea4seaqeyz6zikyr2bqbhy6mrocoqwagx45vlbpsbem7euqv5mf3hrvn2fy") + require.NoError(t, err) + link := buildCidLink(eventCid) + cidEventType := ipldEncode(t, basicnode.Prototype.String.NewBuilder(), "sector_activated") + cidEventData := ipldEncode(t, basicnode.Prototype.Link.NewBuilder(), link) + + // nullable cid event data + nullableCidEventType := ipldEncode(t, basicnode.Prototype.String.NewBuilder(), "sector_activated") + b := basicnode.Prototype__Any{}.NewBuilder() + err = b.AssignNull() + require.NoError(t, err) + nullableCidEventData, err := ipld.Encode(b.Build(), dagcbor.Encode) + require.NoError(t, err) + + // bigInt event data + bigInt, err := filBig.FromString("12345678901234567891234567890123456789012345678901234567890") + require.NoError(t, err) + bigIntEventType := ipldEncode(t, basicnode.Prototype.String.NewBuilder(), "verifier_balance") + tmp, err := bigInt.Bytes() + require.NoError(t, err) + bigIntEventData := ipldEncode(t, basicnode.Prototype.Bytes.NewBuilder(), tmp) + + largeInt := math.MaxInt64 + largeIntEventData := ipldEncode(t, basicnode.Prototype.Int.NewBuilder(), int64(largeInt)) + + smallInt := 10 + smallIntEventData := ipldEncode(t, basicnode.Prototype.Int.NewBuilder(), int64(smallInt)) + + negativeInt := -10 + negativeIntEventData := ipldEncode(t, basicnode.Prototype.Int.NewBuilder(), int64(negativeInt)) + + veryNegativeInt := math.MinInt64 + veryNegativeIntEventData := ipldEncode(t, basicnode.Prototype.Int.NewBuilder(), int64(veryNegativeInt)) tb := []struct { name string @@ -733,7 +787,262 @@ func TestParser_ParseNativeEvents_FVM(t *testing.T) { 1: { "flags": 3, "key": "data", - "value": "dGVzdCBkYXRh", + "value": "dGVzdF9kYXRh", + }, + }, + }, + { + name: "success native negative int event entries", + emitter: filAddress, + entries: []filTypes.EventEntry{ + { + Flags: 0x03, + Key: "$type", + Codec: 0x51, + Value: eventType, + }, + { + Flags: 0x03, + Key: "expiry", + Codec: 0x51, + Value: negativeIntEventData, + }, + }, + wantMetadata: map[int]map[string]any{ + 0: { + "flags": 3, + "key": "$type", + "value": "market_deals_event", + }, + 1: { + "flags": 3, + "key": "expiry", + "value": negativeInt, + }, + }, + }, + { + name: "success native very negative int event entries", + emitter: filAddress, + entries: []filTypes.EventEntry{ + { + Flags: 0x03, + Key: "$type", + Codec: 0x51, + Value: eventType, + }, + { + Flags: 0x03, + Key: "expiry", + Codec: 0x51, + Value: veryNegativeIntEventData, + }, + }, + wantMetadata: map[int]map[string]any{ + 0: { + "flags": 3, + "key": "$type", + "value": "market_deals_event", + }, + 1: { + "flags": 3, + "key": "expiry", + "value": fmt.Sprint(veryNegativeInt), + }, + }, + }, + { + name: "success native small int event entries", + emitter: filAddress, + entries: []filTypes.EventEntry{ + { + Flags: 0x03, + Key: "$type", + Codec: 0x51, + Value: eventType, + }, + { + Flags: 0x03, + Key: "expiry", + Codec: 0x51, + Value: smallIntEventData, + }, + }, + wantMetadata: map[int]map[string]any{ + 0: { + "flags": 3, + "key": "$type", + "value": "market_deals_event", + }, + 1: { + "flags": 3, + "key": "expiry", + "value": smallInt, + }, + }, + }, + { + name: "success native large int event entries", + emitter: filAddress, + entries: []filTypes.EventEntry{ + { + Flags: 0x03, + Key: "$type", + Codec: 0x51, + Value: eventType, + }, + { + Flags: 0x03, + Key: "expiry", + Codec: 0x51, + Value: largeIntEventData, + }, + }, + wantMetadata: map[int]map[string]any{ + 0: { + "flags": 3, + "key": "$type", + "value": "market_deals_event", + }, + 1: { + "flags": 3, + "key": "expiry", + "value": fmt.Sprint(largeInt), + }, + }, + }, + { + name: "success native bigInt event entries", + emitter: filAddress, + entries: []filTypes.EventEntry{ + { + Flags: 0x03, + Key: "$type", + Codec: 0x51, + Value: bigIntEventType, + }, + { + Flags: 0x03, + Key: "balance", + Codec: 0x51, + Value: bigIntEventData, + }, + }, + wantMetadata: map[int]map[string]any{ + 0: { + "flags": 3, + "key": "$type", + "value": "verifier_balance", + }, + 1: { + "flags": 3, + "key": "balance", + "value": "12345678901234567891234567890123456789012345678901234567890", + }, + }, + }, + { + name: "succes native cid event entries", + emitter: filAddress, + entries: []filTypes.EventEntry{ + { + Flags: 0x03, + Key: "$type", + Codec: 0x51, + Value: cidEventType, + }, + { + Flags: 0x03, + Key: "piece_cid", + Codec: 0x51, + Value: cidEventData, + }, + }, + wantMetadata: map[int]map[string]any{ + 0: { + "flags": 3, + "key": "$type", + "value": "sector_activated", + }, + 1: { + "flags": 3, + "key": "piece_cid", + "value": map[string]any{ + "/": "baga6ea4seaqeyz6zikyr2bqbhy6mrocoqwagx45vlbpsbem7euqv5mf3hrvn2fy", + }, + }, + }, + }, + { + name: "succes native nullable cid event entries", + emitter: filAddress, + entries: []filTypes.EventEntry{ + { + Flags: 0x03, + Key: "$type", + Codec: 0x51, + Value: nullableCidEventType, + }, + { + Flags: 0x03, + Key: "unsealed_cid", + Codec: 0x51, + Value: nullableCidEventData, + }, + }, + wantMetadata: map[int]map[string]any{ + 0: { + "flags": 3, + "key": "$type", + "value": "sector_activated", + }, + 1: { + "flags": 3, + "key": "unsealed_cid", + "value": nil, + }, + }, + }, + { + name: "succes native nullable cid and valid cid event entries", + emitter: filAddress, + entries: []filTypes.EventEntry{ + { + Flags: 0x03, + Key: "$type", + Codec: 0x51, + Value: nullableCidEventType, + }, + { + Flags: 0x03, + Key: "unsealed_cid", + Codec: 0x51, + Value: nullableCidEventData, + }, + { + Flags: 0x03, + Key: "piece_cid", + Codec: 0x51, + Value: cidEventData, + }, + }, + wantMetadata: map[int]map[string]any{ + 0: { + "flags": 3, + "key": "$type", + "value": "sector_activated", + }, + 1: { + "flags": 3, + "key": "unsealed_cid", + "value": nil, + }, + 2: { + "flags": 3, + "key": "piece_cid", + "value": map[string]any{ + "/": "baga6ea4seaqeyz6zikyr2bqbhy6mrocoqwagx45vlbpsbem7euqv5mf3hrvn2fy", + }, }, }, }, @@ -759,9 +1068,9 @@ func TestParser_ParseNativeEvents_FVM(t *testing.T) { fmt.Println(err) return } - assert.NoError(t, err) - assert.NotNil(t, events) - assert.NotEmpty(t, events.ParsedEvents) + require.NoError(t, err) + require.NotNil(t, events) + require.NotEmpty(t, events.ParsedEvents) gotMetadata := map[int]map[string]any{} err = json.Unmarshal([]byte(events.ParsedEvents[0].Metadata), &gotMetadata) @@ -774,8 +1083,8 @@ func TestParser_ParseNativeEvents_FVM(t *testing.T) { } assert.EqualValues(t, tipset.GetCidString(), events.ParsedEvents[0].TipsetCid) assert.EqualValues(t, tt.emitter.String(), events.ParsedEvents[0].Emitter) - if len(tt.entries) > 0 { // only check for the selector_id if we have entries in the test case - assert.EqualValues(t, "market_deals_event", events.ParsedEvents[0].SelectorID) + if len(tt.entries) > 0 { // only check for the selector_id if we have entries in the test case\ + assert.Regexp(t, "market_deals_event|sector_activated|verifier_balance", events.ParsedEvents[0].SelectorID) } // check if IDs are unique for all events @@ -1158,6 +1467,292 @@ func TestParser_ParseEthLogs(t *testing.T) { } } +func TestParser_MultisigEventsFromTxs(t *testing.T) { + type expectedResults struct { + proposals []types.MultisigProposal + multisigInfo []types.MultisigInfo + } + tests := []struct { + name string + version string + url string + height string + results expectedResults + }{ + { + name: "multisig events height 14107", + version: v1.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "14107", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 14107, MultisigAddress: "f080", ProposalID: 0, Signer: "f0103", ActionType: "Propose", TxTypeToExecute: "AddVerifier", Value: "{\"Address\":\"f1zo7ub42i3s5cutljzjuqwnltt4xxm4y4f7l5s2i\",\"Allowance\":\"100000000000000\"}"}, + }, + multisigInfo: []types.MultisigInfo{}, + }, + }, + { + name: "multisig events height 1467665", + version: v2.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "1467665", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 1467665, MultisigAddress: "f080", ProposalID: 11, Signer: "f018896", ActionType: "Approve", TxTypeToExecute: "", Value: "{\"ID\":11,\"ProposalHash\":\"/jgVZzOjfHFnrI5K514wyJ+WSVNtLQhthbCrDsX+Dmg=\"}"}, + }, + multisigInfo: []types.MultisigInfo{}, + }, + }, + { + name: "multisig events height 197673", + version: v2.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "197673", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 197673, MultisigAddress: "f03735", ProposalID: 1, Signer: "f01014", ActionType: "Approve", TxTypeToExecute: "", Value: "{\"ID\":1,\"ProposalHash\":null}"}, + }, + multisigInfo: []types.MultisigInfo{ + {Height: 197673, MultisigAddress: "f03735", TxCid: "bafy2bzacedr3hke3xt2jvtret2yalvhkpctefwsgddqyziggcfmgurd7igqaq", Signer: "f03735", ActionType: "AddSigner", Value: "{\"Signer\":\"f15xwdubazj7aft6ylmiw54fa27zyyl3rpc6olgcy\",\"Increase\":false}"}, + }, + }, + }, + { + name: "multisig events height 78689", + version: v1.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "78689", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 78689, MultisigAddress: "f02412", ProposalID: 0, Signer: "f02252", ActionType: "Propose", TxTypeToExecute: "WithdrawBalance", Value: "{\"AmountRequested\":\"3300000000000000000\"}"}, + }, + multisigInfo: []types.MultisigInfo{}, + }, + }, + { + name: "multisig events height 47645", + version: v1.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "47645", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 47645, MultisigAddress: "f22ny34zaozvfsffk445tazmohsvygits3763xpuy", ProposalID: 1, Signer: "f01148", ActionType: "Propose", TxTypeToExecute: "ChangeNumApprovalsThreshold", Value: "{\"NewThreshold\":3}"}, + }, + multisigInfo: []types.MultisigInfo{}, + }, + }, + { + name: "multisig events height 39035", + version: v1.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "39035", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 39035, MultisigAddress: "f23pa4gt4jgkl55drdyzb7dscjzdfh725u45xzwsy", ProposalID: 1, Signer: "f01717", ActionType: "Propose", TxTypeToExecute: "Unknown", Value: "{\"Params\":{\"Method\":\"\",\"Params\":null,\"To\":\"f01174\",\"Value\":\"0\"},\"Return\":{\"Applied\":false,\"Code\":0,\"Ret\":null,\"TxnID\":1}}"}, + }, + multisigInfo: []types.MultisigInfo{}, + }, + }, + { + name: "multisig events height 47635", + version: v1.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "47635", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 47635, MultisigAddress: "f22ny34zaozvfsffk445tazmohsvygits3763xpuy", ProposalID: 0, Signer: "f01148", ActionType: "Propose", TxTypeToExecute: "AddSigner", Value: "{\"Increase\":false,\"Signer\":\"f3vyx6j6jwrpw4dfspselowh6p4sg6cewgykfvnyomtma5eh4exgkkj4my6ki2sax7zdiavi2wbt3dbet3svxq\"}"}, + }, + multisigInfo: []types.MultisigInfo{}, + }, + }, + { + name: "multisig events height 38940", + version: v1.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "38940", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 38940, MultisigAddress: "f23pa4gt4jgkl55drdyzb7dscjzdfh725u45xzwsy", ProposalID: 0, Signer: "f01410", ActionType: "Propose", TxTypeToExecute: "Send", Value: "{\"Value\":\"30000000000000000\"}"}, + }, + multisigInfo: []types.MultisigInfo{}, + }, + }, + { + name: "multisig events height 1698055", + version: v2.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "1698055", + results: expectedResults{ + proposals: []types.MultisigProposal{}, + multisigInfo: []types.MultisigInfo{ + {Height: 1698055, MultisigAddress: "f0123724", Signer: "f01", ActionType: "Constructor", TxCid: "bafy2bzacednj5rv7pbdgyvq2ztknz5xj3eazfqanxiy5za6dpb4kgv3vtd72w", Value: "{\"Signers\":[\"f1fik4crqpv33laa6gvf23vz3sjpioka4go47e2hi\",\"f1hjvq6aays3ohfzxo7sw353esyscnbzabblxs2pq\",\"f3vwrfmyc6wnomefflu2rcejrbvi4zmczq2pcibf2rl7udbwbf4eyjkpftwyhdigevfprnj5g32h5liaxf56qq\"],\"NumApprovalsThreshold\":2,\"UnlockDuration\":0,\"StartEpoch\":0}"}, + }, + }, + }, + { + name: "multisig events height 1576593", + version: v2.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "1576593", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 1576593, MultisigAddress: "f2b3v3bp55krpaqz24fxmlgggbz3gaik6fv5f7ryy", ProposalID: 138, Signer: "f091402", ActionType: "Cancel", TxTypeToExecute: "", Value: "{\"ID\":138,\"ProposalHash\":\"vXH0+s6OtR7wEs0aVsxBgB1/bgOqCSoZ/ImHyBlDVcw=\"}"}, + }, + multisigInfo: []types.MultisigInfo{}, + }, + }, + { + name: "multisig events height 1572087", + version: v2.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "1572087", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 1572087, MultisigAddress: "f2b3v3bp55krpaqz24fxmlgggbz3gaik6fv5f7ryy", ProposalID: 105, Signer: "f091402", ActionType: "Propose", TxTypeToExecute: "ChangeNumApprovalsThreshold", Value: "{\"NewThreshold\":2}"}, + }, + multisigInfo: []types.MultisigInfo{ + {Height: 1572087, MultisigAddress: "f2b3v3bp55krpaqz24fxmlgggbz3gaik6fv5f7ryy", TxCid: "bafy2bzacec74jgx36mdxmggmoxbjhub3cfnzvfu7dujagdk3il7ttz7emu4q4", Signer: "f0110268", ActionType: "ChangeNumApprovalsThreshold", Value: "{\"NewThreshold\":2}"}, + }, + }, + }, + { + name: "multisig events height 1552242", + version: v2.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "1552242", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 1552242, MultisigAddress: "f2t7urdjxp5jf3su5qyf4i25encrozjws6k2uxg2i", ProposalID: 3, Signer: "f06067", ActionType: "Approve", TxTypeToExecute: "", Value: "{\"ID\":3,\"ProposalHash\":\"lMtCwZTOT/0X+G4aplxpQN9xyPWvLLwpUObAVRTqUSI=\"}"}, + }, + multisigInfo: []types.MultisigInfo{ + {Height: 1552242, MultisigAddress: "f2t7urdjxp5jf3su5qyf4i25encrozjws6k2uxg2i", TxCid: "bafy2bzacecutjnons7vvzlamg6sekcnmo5hbkfobdo52p2minokt2rz6vgsqy", Signer: "f059513", ActionType: "SwapSigner", Value: "{\"From\":\"f1dywbadna5yyf546mloeoc7gxrzj7n5uog6llv5y\",\"To\":\"f16sfr4wmxu7ouxayxqqmacmgdfqfbasm4qr472fq\"}"}, + }, + }, + }, + { + name: "multisig events height 1352134", + version: v2.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "1352134", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 1352134, MultisigAddress: "f2kpwyxvbr547eaikwjavx6bs4otae3cqbn5u2t2y", ProposalID: 0, Signer: "f019764", ActionType: "Propose", TxTypeToExecute: "LockBalance", Value: "{\"Amount\":\"1000000000000000000\",\"StartEpoch\":1352039,\"UnlockDuration\":876000}"}, + }, + multisigInfo: []types.MultisigInfo{ + {Height: 1352134, MultisigAddress: "f2kpwyxvbr547eaikwjavx6bs4otae3cqbn5u2t2y", Signer: "f066958", ActionType: "LockBalance", TxCid: "bafy2bzacecfvtxvsfkjrj6odvuii7m5bf52vqni66nwk4clwp5j5x6ovothco", Value: "{\"StartEpoch\":1352039,\"UnlockDuration\":876000,\"Amount\":\"1000000000000000000\"}"}, + }, + }, + }, + { + name: "multisig events height 1334035", + version: v2.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "1334035", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 1334035, MultisigAddress: "f2h4xqc7krcpfulaqch6hxphsp6ze5fwobfrpur2i", ProposalID: 2, Signer: "f06068", ActionType: "Approve", TxTypeToExecute: "", Value: "{\"ID\":2,\"ProposalHash\":\"V5xMhdHMYFd7uwnILnd1BeH6SoksOKTI4KtUyZXOS4k=\"}"}, + }, + multisigInfo: []types.MultisigInfo{ + {Height: 1334035, TxCid: "bafy2bzaceasprlgdy4dbb2cxzwo4opofxk26vkpw3fe3qf5oxj2yjsb7scjoq", MultisigAddress: "f2h4xqc7krcpfulaqch6hxphsp6ze5fwobfrpur2i", Signer: "f063654", ActionType: "RemoveSigner", Value: "{\"Signer\":\"f1fbagfbmhk52hhbih2yt2jixkbisoqtrg4k2kn7a\",\"Decrease\":true}"}, + }, + }, + }, + { + name: "multisig events height 1289201", + version: v2.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "1289201", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 1289201, MultisigAddress: "f064773", ProposalID: 0, Signer: "f064766", ActionType: "Propose", TxTypeToExecute: "SwapSigner", Value: "{\"From\":\"f3vwq5mw6sagjzqap73q56xayzmnrlqpvlecgcduwqmpsr33cngoszviq4eeet7gc5j3he2kf34hmskecjvqva\",\"To\":\"f3sg5mydbqdszt6wld3sjofhotutji5r2vbi5nvraybulexajcqg2fdas6sq7oiihdeqmw7ii3xdzlx723oeja\"}"}, + }, + multisigInfo: []types.MultisigInfo{}, + }, + }, + { + name: "multisig events height 1258459", + version: v2.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "1258459", + results: expectedResults{ + proposals: []types.MultisigProposal{}, + multisigInfo: []types.MultisigInfo{ + {Height: 1258459, MultisigAddress: "f063814", TxCid: "bafy2bzaced6uosdwea2ztyao56umwjza5qzpveg73afohzh3wgoclgtfhtpek", Signer: "f01", ActionType: "Constructor", Value: "{\"Signers\":[\"f16xlkjp3dcfrsb257duoqfgj7glo2uvvgxyy4gmy\",\"f1dywbadna5yyf546mloeoc7gxrzj7n5uog6llv5y\",\"f1fbagfbmhk52hhbih2yt2jixkbisoqtrg4k2kn7a\"],\"NumApprovalsThreshold\":2,\"UnlockDuration\":0,\"StartEpoch\":0}"}, + }, + }, + }, + { + name: "multisig events height 1256171", + version: v2.NodeVersionsSupported[0], + url: calibNextNodeUrl, + height: "1256171", + results: expectedResults{ + proposals: []types.MultisigProposal{ + {Height: 1256171, MultisigAddress: "f063719", ProposalID: 1, Signer: "f063720", ActionType: "Propose", TxTypeToExecute: "RemoveSigner", Value: "{\"Decrease\":false,\"Signer\":\"f1bsqp2nixftm5kacppzrsjkv62ot3kckucthu7ca\"}"}, + }, + multisigInfo: []types.MultisigInfo{ + {Height: 1256171, MultisigAddress: "f063719", TxCid: "bafy2bzacecuhvthgttyv7q3q53p4lqhfkkdh2wktaxywtfarofehqvtsgifnw", Signer: "f063719", ActionType: "RemoveSigner", Value: "{\"Signer\":\"f1bsqp2nixftm5kacppzrsjkv62ot3kckucthu7ca\",\"Decrease\":false}"}, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + lib := getLib(t, tt.url) + + tipset, err := readTipset(tt.height) + require.NoError(t, err) + ethlogs, err := readEthLogs(tt.height) + require.NoError(t, err) + traces, err := readGzFile(tracesFilename(tt.height)) + require.NoError(t, err) + + logger, err := zap.NewDevelopment() + require.NoError(t, err) + + p, err := NewFilecoinParser(lib, getCacheDataSource(t, tt.url), logger) + require.NoError(t, err) + + txsData := types.TxsData{ + EthLogs: ethlogs, + Tipset: tipset, + Traces: traces, + Metadata: types.BlockMetadata{NodeInfo: types.NodeInfo{NodeMajorMinorVersion: tt.version}}, + } + + parsedResult, err := p.ParseTransactions(context.Background(), txsData) + require.NoError(t, err) + require.NotNil(t, parsedResult.Txs) + + tipsetCid := txsData.Tipset.GetCidString() + tipsetKey := txsData.Tipset.Key() + events, err := p.ParseMultisigEvents(context.Background(), parsedResult.Txs, tipsetCid, tipsetKey) + require.NoError(t, err) + require.NotNil(t, events) + + require.Len(t, events.Proposals, len(tt.results.proposals), fmt.Sprintf("Expected %d proposals, but got %d", len(tt.results.proposals), len(events.Proposals))) + for i, expected := range tt.results.proposals { + assert.Equal(t, expected.MultisigAddress, events.Proposals[i].MultisigAddress, fmt.Sprintf("Mismatch in MultisigAddress at proposal index %d: expected %s, got %s", i, expected.MultisigAddress, events.Proposals[i].MultisigAddress)) + assert.Equal(t, expected.ProposalID, events.Proposals[i].ProposalID, fmt.Sprintf("Mismatch in ProposalID at proposal index %d: expected %d, got %d", i, expected.ProposalID, events.Proposals[i].ProposalID)) + assert.Equal(t, expected.Signer, events.Proposals[i].Signer, fmt.Sprintf("Mismatch in Signer at proposal index %d: expected %s, got %s", i, expected.Signer, events.Proposals[i].Signer)) + assert.Equal(t, expected.ActionType, events.Proposals[i].ActionType, fmt.Sprintf("Mismatch in ActionType at proposal index %d: expected %s, got %s", i, expected.ActionType, events.Proposals[i].ActionType)) + assert.Equal(t, expected.TxTypeToExecute, events.Proposals[i].TxTypeToExecute, fmt.Sprintf("Mismatch in TxTypeToExecute at proposal index %d: expected %s, got %s", i, expected.TxTypeToExecute, events.Proposals[i].TxTypeToExecute)) + assert.Equal(t, expected.Value, events.Proposals[i].Value, fmt.Sprintf("Mismatch in Value at proposal index %d: expected %s, got %s", i, expected.Value, events.Proposals[i].Value)) + } + + require.Len(t, events.MultisigInfo, len(tt.results.multisigInfo), fmt.Sprintf("Expected %d multisig info entries, but got %d", len(tt.results.multisigInfo), len(events.MultisigInfo))) + for i, expected := range tt.results.multisigInfo { + assert.Equal(t, expected.MultisigAddress, events.MultisigInfo[i].MultisigAddress, fmt.Sprintf("Mismatch in MultisigAddress at multisig info index %d: expected %s, got %s", i, expected.MultisigAddress, events.MultisigInfo[i].MultisigAddress)) + assert.Equal(t, expected.TxCid, events.MultisigInfo[i].TxCid, fmt.Sprintf("Mismatch in TxCid at multisig info index %d: expected %s, got %s", i, expected.TxCid, events.MultisigInfo[i].TxCid)) + assert.Equal(t, expected.Signer, events.MultisigInfo[i].Signer, fmt.Sprintf("Mismatch in Signer at multisig info index %d: expected %s, got %s", i, expected.Signer, events.MultisigInfo[i].Signer)) + assert.Equal(t, expected.ActionType, events.MultisigInfo[i].ActionType, fmt.Sprintf("Mismatch in ActionType at multisig info index %d: expected %s, got %s", i, expected.ActionType, events.MultisigInfo[i].ActionType)) + assert.Equal(t, expected.Value, events.MultisigInfo[i].Value, fmt.Sprintf("Mismatch in Value at multisig info index %d: expected %s, got %s", i, expected.Value, events.MultisigInfo[i].Value)) + } + }) + } +} + func TestParseGenesis(t *testing.T) { network := "mainnet" genesisBalances, genesisTipset, err := getStoredGenesisData(network) diff --git a/tools/events/decode.go b/tools/events/decode.go new file mode 100644 index 00000000..22fa0809 --- /dev/null +++ b/tools/events/decode.go @@ -0,0 +1,93 @@ +package event_tools + +import ( + "fmt" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/lotus/chain/types" + "github.com/ipfs/go-cid" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec/dagcbor" + "github.com/ipld/go-ipld-prime/datamodel" + basicnode "github.com/ipld/go-ipld-prime/node/basic" + "math" + "regexp" +) + +const ( + maxJSONNumber = math.MaxInt32 + minJSONNumber = math.MinInt32 +) + +var ( + // - https://github.com/filecoin-project/lotus/blob/6e13eac5d51f08d964f1338d9fab7cca42014e5c/documentation/en/actor-events-api.md?plain=1#L365 + cidRegex = regexp.MustCompile("cid") + // https://github.com/filecoin-project/lotus/blob/6e13eac5d51f08d964f1338d9fab7cca42014e5c/documentation/en/actor-events-api.md?plain=1#L112 + bigintRegex = regexp.MustCompile("balance") +) + +// decode does an ipld decode of the entry.Value using dagcbor +func decode(entry types.EventEntry) (datamodel.Node, error) { + n, err := ipld.Decode(entry.Value, dagcbor.Decode) + if err != nil { + return nil, fmt.Errorf("error ipld decode entry: %w ", err) + } + + if n.Kind() == datamodel.Kind_Int { + val, err := n.AsInt() + if err != nil { + return nil, fmt.Errorf("error ipld node to int : %w ", err) + } + if val > maxJSONNumber || val < minJSONNumber { + return basicnode.NewString(fmt.Sprint(val)), nil + } + } + + return n, nil +} + +// parseBigInt uses the filecoin-project big package to decode a node into a big.Int +// required for the verifier_balance event +// https://github.com/filecoin-project/lotus/blob/6e13eac5d51f08d964f1338d9fab7cca42014e5c/documentation/en/actor-events-api.md?plain=1#L112 +func parseBigInt(n datamodel.Node) (any, error) { + hexEncodedInt, err := n.AsBytes() + if err != nil { + return nil, fmt.Errorf("error converting ipld node to string: %w", err) + } + + bigInt, err := big.FromBytes(hexEncodedInt) + if err != nil { + return nil, fmt.Errorf("error converting hex encoded bigint to big.Int: %w", err) + } + return bigInt.String(), nil +} + +// parseCid parses an ipld node into the correct cid implementation. +// special cases include entries that have a CID as a value. +// CIDs are represented as an ipld.Link which needs an extra step of decoding the CID +// to get the correct JSON representation. +// Current edge case entry keys: unsealed-cid,piece-cid +// References: +// - https://github.com/filecoin-project/lotus/blob/6e13eac5d51f08d964f1338d9fab7cca42014e5c/documentation/en/actor-events-api.md?plain=1#L365 +func parseCid(n datamodel.Node) (any, error) { + if n.Kind() == datamodel.Kind_Null { + // nullable CIDs that show up in unsealed_cid are represented as Null + // - https://github.com/filecoin-project/lotus/blob/5dffc05a30894283287345d61b6578be7897ee4b/itests/direct_data_onboard_verified_test.go#L194 + return nil, nil + } + // - https://github.com/filecoin-project/lotus/blob/5dffc05a30894283287345d61b6578be7897ee4b/itests/direct_data_onboard_verified_test.go#L201 + if n.Kind() != datamodel.Kind_Link { + return nil, fmt.Errorf("unexpected datamodel kind for cid: %s ,expected: link", n.Kind()) + } + + link, err := n.AsLink() + if err != nil { + return nil, fmt.Errorf("error converting cid ipld node to link : %s : %w", n.Kind(), err) + } + + c, err := cid.Decode(link.String()) + if err != nil { + return nil, fmt.Errorf("error decoding %s to cid: %w", link.String(), err) + } + + return c, nil +} diff --git a/tools/events/events.go b/tools/events/events.go index 3071cab6..4daa5c58 100644 --- a/tools/events/events.go +++ b/tools/events/events.go @@ -13,10 +13,7 @@ import ( filTypes "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/ipfs/go-cid" - "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/codec/dagcbor" "github.com/ipld/go-ipld-prime/datamodel" - "github.com/multiformats/go-multicodec" "github.com/zondax/fil-parser/parser/helper" "github.com/zondax/fil-parser/tools" "github.com/zondax/fil-parser/types" @@ -200,6 +197,10 @@ func buildEVMEventMetaData[T interface{ string | ethtypes.EthHash }](data []byte func parseNativeEventEntry(eventType string, entries []filTypes.EventEntry) (map[int]map[string]any, error) { parsedEntries := map[int]map[string]any{} + var ( + edgeCaseEntries []int + ) + for idx, entry := range entries { parsedEntry := map[string]any{ parsedEntryKey: entry.Key, @@ -223,14 +224,21 @@ func parseNativeEventEntry(eventType string, entries []filTypes.EventEntry) (map } parsedEntry["value"] = selectorHash.String() case types.EventTypeNative: - if entry.Codec != uint64(multicodec.Cbor) { - break - } - n, err := ipld.Decode(entry.Value, dagcbor.Decode) + var ( + err error + parsedValue datamodel.Node + ) + parsedValue, err = decode(entry) if err != nil { - return nil, fmt.Errorf("error ipld decode native event: %w ", err) + zap.S().Error("error ipld decode native event: ", err, entry.Key, entry.Codec, entry.Value) + return nil, fmt.Errorf("error decoding native event: %w ", err) + } + + // if the entry key is a CID or a bigInt, we need to decode the parsedValue further + if cidRegex.MatchString(entry.Key) || bigintRegex.MatchString(entry.Key) { + edgeCaseEntries = append(edgeCaseEntries, idx) } - parsedEntry[parsedEntryValue] = n + parsedEntry[parsedEntryValue] = parsedValue } } parsedEntry[parsedEntryFlags] = entry.Flags @@ -240,5 +248,30 @@ func parseNativeEventEntry(eventType string, entries []filTypes.EventEntry) (map parsedEntries[idx] = parsedEntry } + + // decode the entry values for the CIDs and BigInts + for _, idx := range edgeCaseEntries { + var ( + err error + data any + ) + + key := parsedEntries[idx][parsedEntryKey].(string) + value := parsedEntries[idx][parsedEntryValue].(datamodel.Node) + switch { + case cidRegex.MatchString(key): + data, err = parseCid(value) + case bigintRegex.MatchString(key): + data, err = parseBigInt(value) + default: + err = fmt.Errorf("unable to retrieve %s from evm event entry", key) + } + + if err != nil { + return nil, fmt.Errorf("error parsing native event: %w", err) + } + parsedEntries[idx][parsedEntryValue] = data + } + return parsedEntries, nil } diff --git a/tools/multisig/multisig.go b/tools/multisig/multisig.go new file mode 100644 index 00000000..a3e40510 --- /dev/null +++ b/tools/multisig/multisig.go @@ -0,0 +1,235 @@ +package multisig + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/manifest" + filTypes "github.com/filecoin-project/lotus/chain/types" + "github.com/zondax/fil-parser/actors" + "github.com/zondax/fil-parser/parser" + "github.com/zondax/fil-parser/parser/helper" + "github.com/zondax/fil-parser/tools" + "github.com/zondax/fil-parser/types" + "go.uber.org/zap" +) + +const ( + VERSION = "V1" + MultisigStore = "multisig" + + metadataParams = "Params" + metadataReturn = "Return" + metadataTxnIDField = "TxnID" + metadataIDField = "ID" + metadataMethod = "Method" + metadataValue = "Value" + + txStatusOk = "ok" +) + +var proposeTranslateMap = map[string]string{ + parser.MethodPropose: parser.MethodPropose, + parser.MethodProposeExported: parser.MethodPropose, +} + +var cancelApproveTranslateMap = map[string]string{ + parser.MethodApprove: parser.MethodApprove, + parser.MethodApproveExported: parser.MethodApprove, + parser.MethodCancel: parser.MethodCancel, + parser.MethodCancelExported: parser.MethodCancel, +} + +type EventGenerator interface { + GenerateMultisigEvents(ctx context.Context, transactions []*types.Transaction, tipsetCid string, tipsetKey filTypes.TipSetKey) (*types.MultisigEvents, error) +} + +type eventGenerator struct { + helper *helper.Helper + logger *zap.Logger +} + +func NewEventGenerator(helper *helper.Helper, logger *zap.Logger) EventGenerator { + return &eventGenerator{ + helper: helper, + logger: logger, + } +} + +func (eg *eventGenerator) GenerateMultisigEvents(ctx context.Context, transactions []*types.Transaction, tipsetCid string, tipsetKey filTypes.TipSetKey) (*types.MultisigEvents, error) { + events := &types.MultisigEvents{ + Proposals: []*types.MultisigProposal{}, + MultisigInfo: []*types.MultisigInfo{}, + } + + for _, tx := range transactions { + if !strings.EqualFold(tx.Status, txStatusOk) { + eg.logger.Sugar().Debug("failed tx found, skipping it") + continue + } + + metadata, err := tools.ParseTxMetadata(tx.TxMetadata) + if err != nil { + return nil, err + } + + isProposalType := isProposalType(tx.TxType) + if isProposalType { + proposal := eg.createProposal(ctx, tx, metadata, tipsetCid) + events.Proposals = append(events.Proposals, proposal) + } else { + // Only consider transactions where tx.TxTo is a multisig address + // because we only take into account those that change the state of the multisig + + addrTo, err := address.NewFromString(tx.TxTo) + if err != nil { + eg.logger.Sugar().Errorf("could not parse address. Err: %s", err) + continue + } + + actorName, err := eg.helper.GetActorNameFromAddress(addrTo, int64(tx.Height), tipsetKey) + if err != nil { + eg.logger.Sugar().Errorf("could not get actor name from address. Err: %s", err) + continue + } + if !strings.EqualFold(actorName, manifest.MultisigKey) { + continue + } + + multisigInfo, err := eg.createMultisigInfo(ctx, tx, tipsetCid) + if err != nil { + // TODO: Metric + continue + } + events.MultisigInfo = append(events.MultisigInfo, multisigInfo) + } + } + + return events, nil +} + +func (eg *eventGenerator) createProposal(ctx context.Context, tx *types.Transaction, metadata map[string]interface{}, tipsetCid string) *types.MultisigProposal { + proposal := &types.MultisigProposal{ + MultisigAddress: tx.TxTo, + Height: tx.Height, + TxCid: tx.TxCid, + Signer: tx.TxFrom, + } + + eg.processProposalParams(ctx, metadata, tx.TxType, proposal) + if ret, ok := metadata[metadataReturn].(map[string]interface{}); ok { + if txnID, ok := ret[metadataTxnIDField].(float64); ok { + proposal.ProposalID = int64(txnID) + } + } + + proposal.ID = tools.BuildId(tipsetCid, proposal.MultisigAddress, fmt.Sprint(proposal.ProposalID), fmt.Sprint(tx.Height), tx.TxType) + return proposal +} + +func (eg *eventGenerator) processProposalParams(ctx context.Context, metadata map[string]interface{}, txType string, proposal *types.MultisigProposal) { + if isCancelOrApprove(txType) { + proposal.ActionType = cancelApproveTranslateMap[txType] + proposal.TxTypeToExecute = "" + + metadata[metadataParams] = eg.parseParamsString(ctx, metadata) + + if params, ok := metadata[metadataParams].(map[string]interface{}); ok { + if metadataID, ok := params[metadataIDField].(float64); ok { + proposal.ProposalID = int64(metadataID) + } + eg.processNestedParams(ctx, params, proposal) + } + } else { + proposal.ActionType = proposeTranslateMap[txType] + proposal.TxTypeToExecute = parser.MethodUnknown + + params, ok := metadata[metadataParams].(map[string]interface{}) + if !ok { + return + } + eg.processNestedParams(ctx, params, proposal) + + method, _ := params[metadataMethod].(string) + if method != "" { + proposal.TxTypeToExecute = method + return + } + + metadataJSON, _ := json.Marshal(metadata) + eg.logger.Sugar().Debug(ctx, fmt.Sprintf("unknown method with metadata %v", string(metadataJSON))) + proposal.Value = string(metadataJSON) + } +} + +func (eg *eventGenerator) processNestedParams(ctx context.Context, params map[string]interface{}, proposal *types.MultisigProposal) { + if nestedParams, ok := params[metadataParams].(map[string]interface{}); ok { + jsonParams, err := json.Marshal(nestedParams) + if err != nil { + eg.logger.Sugar().Error(ctx, fmt.Sprintf("Error marshaling nested params: %v", err)) + return + } + proposal.Value = string(jsonParams) + return + } + + if valueStr, ok := params[metadataValue].(string); ok { + params = map[string]interface{}{"Value": valueStr} + } + + jsonParams, err := json.Marshal(params) + if err != nil { + eg.logger.Sugar().Error(ctx, fmt.Sprintf("Error marshaling params: %v", err)) + return + } + + eg.logger.Sugar().Debug(ctx, fmt.Sprintf("zero value with params: %v", string(jsonParams))) + proposal.Value = string(jsonParams) + +} + +func (eg *eventGenerator) createMultisigInfo(ctx context.Context, tx *types.Transaction, tipsetCid string) (*types.MultisigInfo, error) { + value, err := actors.ParseMultisigMetadata(tx.TxType, tx.TxMetadata) + if err != nil { + eg.logger.Sugar().Error(ctx, fmt.Sprintf("Multisig error parsing metadata: %s", err.Error())) + value = tx.TxMetadata // if there is an error then we need to store the raw metadata + } + + b, err := json.Marshal(value) + if err != nil { + eg.logger.Sugar().Error(ctx, fmt.Sprintf("Multisig error marshaling value: %s", err.Error())) + return nil, err + } + + return &types.MultisigInfo{ + ID: tools.BuildId(tipsetCid, tx.TxTo, fmt.Sprint(tx.Height), tx.TxCid, tx.TxType), + MultisigAddress: tx.TxTo, + Height: tx.Height, + TxCid: tx.TxCid, + Signer: tx.TxFrom, + ActionType: tx.TxType, + Value: string(b), + }, nil +} + +func (eg *eventGenerator) parseParamsString(ctx context.Context, metadata map[string]interface{}) map[string]interface{} { + var params map[string]interface{} + if paramsStr, ok := metadata[metadataParams].(string); ok { + if err := json.Unmarshal([]byte(paramsStr), ¶ms); err != nil { + eg.logger.Sugar().Error(fmt.Sprintf("Error deserializing params string: %v", err)) + return nil + } + } + return params +} + +func isProposalType(txType string) bool { + return proposeTranslateMap[txType] != "" || cancelApproveTranslateMap[txType] != "" +} + +func isCancelOrApprove(txType string) bool { + return cancelApproveTranslateMap[txType] != "" +} diff --git a/tools/tools.go b/tools/tools.go index bf4f607b..7b0dada9 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -3,6 +3,7 @@ package tools import ( "bytes" "crypto/sha256" + "encoding/json" "fmt" "github.com/zondax/fil-parser/parser" @@ -170,3 +171,12 @@ func SetNodeMetadataOnEvents(events []types.Event, metadata types.BlockMetadata, return newEvents } + +func ParseTxMetadata(txMetadata string) (map[string]interface{}, error) { + var metadata map[string]interface{} + err := json.Unmarshal([]byte(txMetadata), &metadata) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal TxMetadata: %v", err) + } + return metadata, nil +}