diff --git a/go.mod b/go.mod index ebe97ca8..81e10aaf 100644 --- a/go.mod +++ b/go.mod @@ -6,14 +6,12 @@ toolchain go1.22.2 require ( github.com/agiledragon/gomonkey/v2 v2.11.0 - github.com/blocto/solana-go-sdk v1.26.0 github.com/cockroachdb/pebble v1.1.0 github.com/ethereum/go-ethereum v1.14.6 github.com/fatih/color v1.16.0 github.com/gin-gonic/gin v1.9.1 github.com/google/uuid v1.6.0 github.com/ipfs/go-ipfs-api v0.7.0 - github.com/jmoiron/sqlx v1.3.5 github.com/lib/pq v1.10.9 github.com/libp2p/go-libp2p v0.32.2 github.com/libp2p/go-libp2p-kad-dht v0.25.2 @@ -24,9 +22,6 @@ require ( github.com/prometheus/client_golang v1.18.0 github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.8.4 - github.com/tablelandnetwork/basin-cli v0.0.11 - github.com/tidwall/gjson v1.17.0 - go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.1 @@ -39,7 +34,6 @@ require ( require github.com/cespare/cp v1.1.1 // indirect require ( - filippo.io/edwards25519 v1.0.0-rc.1 // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect @@ -132,6 +126,7 @@ require ( github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mattn/go-sqlite3 v1.14.23 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/miekg/dns v1.1.57 // indirect @@ -179,12 +174,11 @@ require ( github.com/spf13/cast v1.5.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tidwall/match v1.1.1 // indirect - github.com/tidwall/pretty v1.2.1 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect + github.com/urfave/cli/v2 v2.25.7 // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel v1.21.0 // indirect @@ -192,6 +186,7 @@ require ( go.opentelemetry.io/otel/trace v1.21.0 // indirect go.uber.org/dig v1.17.1 // indirect go.uber.org/fx v1.20.1 // indirect + go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/arch v0.7.0 // indirect diff --git a/go.sum b/go.sum index e41f9f94..9c9e3d44 100644 --- a/go.sum +++ b/go.sum @@ -42,8 +42,6 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= -filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -65,8 +63,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/blocto/solana-go-sdk v1.26.0 h1:NpLKmP89TPbX35U07kFgfcTibLFcb69NK0wYgEwLfTM= -github.com/blocto/solana-go-sdk v1.26.0/go.mod h1:Xoyhhb3hrGpEQ5rJps5a3OgMwDpmEhrd9bgzFKkkwMs= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= @@ -194,7 +190,6 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= @@ -365,8 +360,6 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -400,7 +393,6 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= @@ -451,7 +443,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0= github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -652,16 +643,7 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tablelandnetwork/basin-cli v0.0.11 h1:lsZcwwrsHKPzSU3Fq6EbqxbetXOlf5p8xoMX/hXa2Ik= -github.com/tablelandnetwork/basin-cli v0.0.11/go.mod h1:MFKI9q5IcgEhCmWHX60nJQCsLpy1PZ5LLTXdno93bmU= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= -github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= @@ -673,7 +655,6 @@ github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3C github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.10 h1:p8Fspmz3iTctJstry1PYS3HVdllxnEzTEsgIgtxTrCk= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= diff --git a/output/ethereum.go b/output/ethereum.go deleted file mode 100644 index 36deb69a..00000000 --- a/output/ethereum.go +++ /dev/null @@ -1,213 +0,0 @@ -package output - -import ( - "context" - "crypto/ecdsa" - "math/big" - "strings" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/pkg/errors" - "github.com/tidwall/gjson" - - "github.com/iotexproject/w3bstream/task" -) - -var ( - errSnarkProofDataMissingFieldSnark = errors.New("missing field `Snark.snark`") - errSnarkProofDataMissingFieldPostStateDigest = errors.New("missing field `Snark.post_state_digest`") - errSnarkProofDataMissingFieldJournal = errors.New("missing field `Snark.journal`") - errMissingReceiverParam = errors.New("missing receiver param") -) - -type ethereumContract struct { - client *ethclient.Client - contractAddress common.Address - receiverAddress string - secretKey *ecdsa.PrivateKey - signer ethtypes.Signer - contractABI abi.ABI - contractMethod abi.Method - contractWhitelist []string -} - -func (e *ethereumContract) Output(proverID uint64, task *task.Task, proof []byte) (string, error) { - - if e.isWhitelist() { - txHash, err := e.sendTX(context.Background(), proof) - if err != nil { - return "", errors.Wrap(err, "failed to send transaction") - } - - return txHash, nil - } - - params := []interface{}{} - for _, a := range e.contractMethod.Inputs { - switch a.Name { - case "proof", "_proof", "data", "_data": - params = append(params, proof) - - case "projectId", "_projectId": - i := new(big.Int).SetUint64(task.ProjectID) - params = append(params, i) - - case "clientId", "_clientId": - params = append(params, task.DeviceID) - - case "proverId", "_proverId": - i := new(big.Int).SetUint64(proverID) - params = append(params, i) - - case "receiver", "_receiver": - if e.receiverAddress == "" { - return "", errMissingReceiverParam - } - params = append(params, common.HexToAddress(e.receiverAddress)) - - case "data_snark", "_data_snark": - valueSeal := gjson.GetBytes(proof, "Snark.snark").String() - if valueSeal == "" { - return "", errSnarkProofDataMissingFieldSnark - } - valueDigest := gjson.GetBytes(proof, "Snark.post_state_digest").String() - if valueDigest == "" { - return "", errSnarkProofDataMissingFieldPostStateDigest - } - valueJournal := gjson.GetBytes(proof, "Snark.journal").String() - if valueJournal == "" { - return "", errSnarkProofDataMissingFieldJournal - } - - abiBytes, err := abi.NewType("bytes", "", nil) - if err != nil { - return "", errors.Wrap(err, "new ethereum accounts abi pack failed") - } - args := abi.Arguments{ - {Type: abiBytes, Name: "proof_snark_seal"}, - {Type: abiBytes, Name: "proof_snark_post_state_digest"}, - {Type: abiBytes, Name: "proof_snark_journal"}, - } - - packed, err := args.Pack([]byte(valueSeal), []byte(valueDigest), []byte(valueJournal)) - if err != nil { - return "", errors.Wrap(err, "ethereum accounts abi pack failed") - } - params = append(params, packed) - - default: - value := gjson.GetBytes(task.Payloads[0], a.Name) - param := value.String() - if param == "" { - return "", errors.Errorf("miss param %s for contract abi", a.Name) - } - switch a.Type.String() { - case "address": - params = append(params, common.HexToAddress(param)) - case "uint256": - i := new(big.Int) - i.SetString(strings.TrimPrefix(param, "0x"), 16) - params = append(params, i) - default: - params = append(params, param) - } - } - } - calldata, err := e.contractABI.Pack(e.contractMethod.Name, params...) - if err != nil { - return "", errors.Wrap(err, "failed to pack by contract abi") - } - - txHash, err := e.sendTX(context.Background(), calldata) - if err != nil { - return "", errors.Wrap(err, "failed to send transaction") - } - - return txHash, nil -} - -func (e *ethereumContract) sendTX(ctx context.Context, data []byte) (string, error) { - sender := crypto.PubkeyToAddress(e.secretKey.PublicKey) - gasPrice, err := e.client.SuggestGasPrice(ctx) - if err != nil { - return "", errors.Wrap(err, "failed to get suggest gas price") - } - msg := ethereum.CallMsg{ - From: sender, - To: &e.contractAddress, - GasPrice: gasPrice, - Data: data, - } - gasLimit, err := e.client.EstimateGas(ctx, msg) - if err != nil { - return "", errors.Wrap(err, "failed to estimate gas") - } - nonce, err := e.client.PendingNonceAt(ctx, sender) - if err != nil { - return "", errors.Wrap(err, "failed to get pending nonce") - } - - tx := ethtypes.NewTx( - ðtypes.LegacyTx{ - Nonce: nonce, - GasPrice: gasPrice, - Gas: gasLimit, - To: &e.contractAddress, - Data: data, - }) - signedTx, err := ethtypes.SignTx(tx, e.signer, e.secretKey) - if err != nil { - return "", errors.Wrap(err, "failed to sign tx") - } - - if err = e.client.SendTransaction(ctx, signedTx); err != nil { - return "", errors.Wrap(err, "failed to send transaction") - } - - return signedTx.Hash().Hex(), nil -} - -func (e *ethereumContract) isWhitelist() bool { - for _, address := range e.contractWhitelist { - if strings.ToLower(e.contractAddress.String()) == strings.ToLower(address) { - return true - } - } - return false -} -func newEthereum(conf EthereumConfig, secretKey string, contractWhitelist string) (*ethereumContract, error) { - if secretKey == "" { - return nil, errors.New("secret key is empty") - } - contractABI, err := abi.JSON(strings.NewReader(conf.ContractAbiJSON)) - if err != nil { - return nil, errors.Wrap(err, "failed to decode contract abi") - } - method, ok := contractABI.Methods[conf.ContractMethod] - if !ok { - return nil, errors.New("the contract method not exist in abi") - } - client, err := ethclient.Dial(conf.ChainEndpoint) - if err != nil { - return nil, errors.Wrapf(err, "dial eth endpoint %s failed", conf.ChainEndpoint) - } - chainID, err := client.ChainID(context.Background()) - if err != nil { - return nil, errors.Wrap(err, "failed to get chain id") - } - return ðereumContract{ - client: client, - secretKey: crypto.ToECDSAUnsafe(common.FromHex(secretKey)), - signer: ethtypes.NewLondonSigner(chainID), - contractAddress: common.HexToAddress(conf.ContractAddress), - receiverAddress: conf.ReceiverAddress, - contractABI: contractABI, - contractMethod: method, - contractWhitelist: strings.Split(contractWhitelist, ","), - }, nil -} diff --git a/output/ethereum_test.go b/output/ethereum_test.go deleted file mode 100644 index 4580fe11..00000000 --- a/output/ethereum_test.go +++ /dev/null @@ -1,440 +0,0 @@ -package output - -import ( - "context" - "crypto/ecdsa" - _ "embed" - "encoding/hex" - "encoding/json" - "math/big" - "testing" - - . "github.com/agiledragon/gomonkey/v2" - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/pkg/errors" - "github.com/stretchr/testify/require" - - "github.com/iotexproject/w3bstream/task" -) - -var ( - testMethodName = "testMethod" - //go:embed testdata/testABI_proofInputOnlyMethod.json - testABIProofInputOnlyMethod string - //go:embed testdata/testABI_projectInputOnlyMethod.json - testABIProjectInputOnlyMethod string - //go:embed testdata/testABI_dataSnarkInputOnlyMethod.json - testABIDataSnarkInputOnlyMethod string - //go:embed testdata/testABI_receiverInputOnlyMethod.json - testABIReceiverInputOnlyMethod string - //go:embed testdata/testABI_otherInputOnlyMethod.json - testABIOtherInputOnlyMethod string - //go:embed testdata/testABI_otherInputOnlyMethod_address.json - testABIOtherInputOnlyMethod_address string - //go:embed testdata/testABI_otherInputOnlyMethod_uint256.json - testABIOtherInputOnlyMethod_uint256 string - - conf = &Config{ - Type: EthereumContract, - Ethereum: EthereumConfig{ - ContractMethod: testMethodName, - }, - } -) - -func patchEthereumContractSendTX(p *Patches, txhash string, err error) *Patches { - return p.ApplyPrivateMethod(ðereumContract{}, "sendTX", - func(contract *ethereumContract, ctx context.Context, data []byte) (string, error) { - return txhash, err - }, - ) -} - -func Test_ethereumContract_Output(t *testing.T) { - r := require.New(t) - txHashRet := "anyTxHash" - - t.Run("BuildParameters", func(t *testing.T) { - t.Run("Proof", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - patchEthereumContractSendTX(p, txHashRet, nil) - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", nil, nil) - - conf.Ethereum.ContractAbiJSON = testABIProofInputOnlyMethod - o, err := New(conf, "1", "", "") - r.NoError(err) - _, ok := o.(*ethereumContract) - r.True(ok) - - txHash, err := o.Output(uint64(0), &task.Task{}, []byte("any proof data")) - r.Equal(txHash, txHashRet) - r.NoError(err) - }) - - t.Run("ProjectID", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - patchEthereumContractSendTX(p, txHashRet, nil) - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", nil, nil) - - conf.Ethereum.ContractAbiJSON = testABIProjectInputOnlyMethod - o, err := New(conf, "1", "", "") - r.NoError(err) - _, ok := o.(*ethereumContract) - r.True(ok) - - txHash, err := o.Output(uint64(0), &task.Task{}, []byte("any proof data")) - r.Equal(txHash, txHashRet) - r.NoError(err) - }) - - t.Run("Receiver", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - patchEthereumContractSendTX(p, txHashRet, nil) - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", nil, nil) - - conf.Ethereum.ContractAbiJSON = testABIReceiverInputOnlyMethod - o, err := New(conf, "1", "", "") - r.NoError(err) - _, ok := o.(*ethereumContract) - r.True(ok) - - t.Run("ReceiverAddressNotInConfig", func(t *testing.T) { - txHash, err := o.Output(uint64(0), &task.Task{}, []byte("any proof data")) - r.Equal(txHash, "") - r.Equal(err, errMissingReceiverParam) - }) - - conf.Ethereum.ReceiverAddress = "0x" - o, err = New(conf, "1", "", "") - r.NoError(err) - _, ok = o.(*ethereumContract) - r.True(ok) - - t.Run("Success", func(t *testing.T) { - txHash, err := o.Output(uint64(0), &task.Task{}, []byte("any proof data")) - r.Equal(txHash, txHashRet) - r.NoError(err) - }) - }) - - t.Run("DataSnark", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - patchEthereumContractSendTX(p, txHashRet, nil) - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", nil, nil) - - conf.Ethereum.ContractAbiJSON = testABIDataSnarkInputOnlyMethod - o, err := New(conf, "1", "", "") - r.NoError(err) - _, ok := o.(*ethereumContract) - r.True(ok) - - t.Run("FailedToDecodeProof", func(t *testing.T) { - txHash, err := o.Output(uint64(0), &task.Task{}, []byte("INVALID_HEX_DECODE")) - r.Equal(txHash, "") - r.Error(err) - }) - proof := &struct { - Snark map[string]string `json:"Snark"` - }{ - Snark: make(map[string]string), - } - t.Run("MissingSnarkField", func(t *testing.T) { - data, err := json.Marshal(proof) - r.NoError(err) - hexdata := hex.EncodeToString(data) - - txHash, err := o.Output(uint64(0), &task.Task{}, []byte(hexdata)) - r.Equal(txHash, "") - r.Equal(err, errSnarkProofDataMissingFieldSnark) - }) - }) - t.Run("Default", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - patchEthereumContractSendTX(p, txHashRet, nil) - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", nil, nil) - - t.Run("MissingMethodNameParam", func(t *testing.T) { - conf.Ethereum.ContractAbiJSON = testABIOtherInputOnlyMethod - o, err := New(conf, "1", "", "") - r.NoError(err) - _, ok := o.(*ethereumContract) - r.True(ok) - - txHash, err := o.Output(uint64(0), &task.Task{ - ProjectID: 1, - Payloads: [][]byte{[]byte(`{"other":""}`)}, - }, nil) - r.Equal(txHash, "") - r.Error(err) - }) - - t.Run("BuildParamsByType", func(t *testing.T) { - t.Run("Address", func(t *testing.T) { - conf.Ethereum.ContractAbiJSON = testABIOtherInputOnlyMethod_address - o, err := New(conf, "1", "", "") - r.NoError(err) - _, ok := o.(*ethereumContract) - r.True(ok) - - txHash, err := o.Output(uint64(0), &task.Task{ - ProjectID: 1, - Payloads: [][]byte{[]byte(`{"other":"any"}`)}, - }, nil) - r.Equal(txHash, txHashRet) - r.NoError(err) - }) - t.Run("Uint256", func(t *testing.T) { - conf.Ethereum.ContractAbiJSON = testABIOtherInputOnlyMethod_uint256 - o, err := New(conf, "1", "", "") - r.NoError(err) - _, ok := o.(*ethereumContract) - r.True(ok) - - txHash, err := o.Output(uint64(0), &task.Task{ - ProjectID: 1, - Payloads: [][]byte{[]byte(`{"other":"any"}`)}, - }, nil) - r.Equal(txHash, txHashRet) - r.NoError(err) - }) - t.Run("Other", func(t *testing.T) { - conf.Ethereum.ContractAbiJSON = testABIOtherInputOnlyMethod - o, err := New(conf, "1", "", "") - r.NoError(err) - _, ok := o.(*ethereumContract) - r.True(ok) - - txHash, err := o.Output(uint64(0), &task.Task{ - ProjectID: 1, - Payloads: [][]byte{[]byte(`{"other":"any"}`)}, - }, nil) - r.NoError(err) - r.Equal(txHash, txHashRet) - }) - }) - }) - }) - t.Run("PackTxData", func(t *testing.T) { - t.Run("FailedToPack", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - patchEthereumContractSendTX(p, txHashRet, nil) - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", nil, nil) - p.ApplyMethodReturn(abi.ABI{}, "Pack", nil, errors.New(t.Name())) - - conf.Ethereum.ContractAbiJSON = testABIProofInputOnlyMethod - o, err := New(conf, "1", "", "") - r.NoError(err) - - txHash, err := o.Output(uint64(0), &task.Task{ - ProjectID: 1, - Payloads: [][]byte{[]byte(`{"other":""}`)}, - }, nil) - - r.Equal(txHash, "") - r.ErrorContains(err, t.Name()) - }) - }) - - t.Run("SendTxData", func(t *testing.T) { - t.Run("FailedToSendTx", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - patchEthereumContractSendTX(p, "", errors.New(t.Name())) - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", nil, nil) - p.ApplyMethodReturn(abi.ABI{}, "Pack", nil, errors.New(t.Name())) - - o, err := New(conf, "1", "", "") - r.NoError(err) - - txHash, err := o.Output(uint64(0), &task.Task{ - ProjectID: 1, - Payloads: [][]byte{[]byte(`{"other":""}`)}, - }, nil) - - r.Equal(txHash, "") - r.ErrorContains(err, t.Name()) - }) - }) - - t.Run("Success", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - patchEthereumContractSendTX(p, txHashRet, nil) - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", nil, nil) - p.ApplyMethodReturn(abi.ABI{}, "Pack", nil, nil) - - o, err := New(conf, "1", "", "") - r.NoError(err) - - txHash, err := o.Output(uint64(0), &task.Task{ - ProjectID: 1, - Payloads: [][]byte{[]byte(`{"other":""}`)}, - }, nil) - r.NoError(err) - r.Equal(txHash, txHashRet) - }) -} - -func Test_ethereumContract_sendTX(t *testing.T) { - r := require.New(t) - - conf.Ethereum.ContractAbiJSON = testABIOtherInputOnlyMethod - ctx := context.Background() - - t.Run("SuggestGasFailed", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", nil, nil) - p.ApplyFuncReturn(crypto.ToECDSAUnsafe, &ecdsa.PrivateKey{}) - p.ApplyFuncReturn(crypto.PubkeyToAddress, common.Address{}) - p.ApplyFuncReturn(common.HexToAddress, common.Address{}) - p.ApplyMethodReturn(ðclient.Client{}, "SuggestGasPrice", nil, errors.New(t.Name())) - - o, err := New(conf, "1", "", "") - r.NoError(err) - contract, ok := o.(*ethereumContract) - r.True(ok) - _, err = contract.sendTX(ctx, nil) - r.ErrorContains(err, t.Name()) - }) - - t.Run("GetNonceFailed", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyFuncReturn(crypto.ToECDSAUnsafe, &ecdsa.PrivateKey{}) - p.ApplyFuncReturn(crypto.PubkeyToAddress, common.Address{}) - p.ApplyFuncReturn(common.HexToAddress, common.Address{}) - p.ApplyMethodReturn(ðclient.Client{}, "SuggestGasPrice", big.NewInt(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", big.NewInt(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "EstimateGas", uint64(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "PendingNonceAt", nil, errors.New(t.Name())) - - o, err := New(conf, "1", "", "") - r.NoError(err) - contract, ok := o.(*ethereumContract) - r.True(ok) - _, err = contract.sendTX(ctx, nil) - r.ErrorContains(err, t.Name()) - }) - - t.Run("EstimateGasFailed", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyFuncReturn(crypto.ToECDSAUnsafe, &ecdsa.PrivateKey{}) - p.ApplyFuncReturn(crypto.PubkeyToAddress, common.Address{}) - p.ApplyFuncReturn(common.HexToAddress, common.Address{}) - p.ApplyMethodReturn(ðclient.Client{}, "SuggestGasPrice", big.NewInt(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", big.NewInt(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "PendingNonceAt", uint64(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "EstimateGas", nil, errors.New(t.Name())) - - o, err := New(conf, "1", "", "") - r.NoError(err) - contract, ok := o.(*ethereumContract) - r.True(ok) - _, err = contract.sendTX(ctx, nil) - r.ErrorContains(err, t.Name()) - }) - - t.Run("SignTxFailed", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyFuncReturn(crypto.ToECDSAUnsafe, &ecdsa.PrivateKey{}) - p.ApplyFuncReturn(crypto.PubkeyToAddress, common.Address{}) - p.ApplyFuncReturn(common.HexToAddress, common.Address{}) - p.ApplyMethodReturn(ðclient.Client{}, "SuggestGasPrice", big.NewInt(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", big.NewInt(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "PendingNonceAt", uint64(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "EstimateGas", uint64(1), nil) - p.ApplyFuncReturn(ethtypes.SignTx, nil, errors.New(t.Name())) - - o, err := New(conf, "1", "", "") - r.NoError(err) - contract, ok := o.(*ethereumContract) - r.True(ok) - _, err = contract.sendTX(ctx, nil) - r.ErrorContains(err, t.Name()) - }) - - t.Run("TransactionFailed", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyFuncReturn(crypto.ToECDSAUnsafe, &ecdsa.PrivateKey{}) - p.ApplyFuncReturn(crypto.PubkeyToAddress, common.Address{}) - p.ApplyFuncReturn(common.HexToAddress, common.Address{}) - p.ApplyMethodReturn(ðclient.Client{}, "SuggestGasPrice", big.NewInt(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", big.NewInt(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "PendingNonceAt", uint64(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "EstimateGas", uint64(1), nil) - p.ApplyFuncReturn(ethtypes.SignTx, nil, nil) - p.ApplyMethodReturn(ðclient.Client{}, "SendTransaction", errors.New(t.Name())) - - o, err := New(conf, "1", "", "") - r.NoError(err) - contract, ok := o.(*ethereumContract) - r.True(ok) - _, err = contract.sendTX(ctx, nil) - r.ErrorContains(err, t.Name()) - }) - - t.Run("TransactionSuccess", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyFuncReturn(crypto.ToECDSAUnsafe, &ecdsa.PrivateKey{}) - p.ApplyFuncReturn(crypto.PubkeyToAddress, common.Address{}) - p.ApplyFuncReturn(common.HexToAddress, common.Address{}) - p.ApplyMethodReturn(ðclient.Client{}, "SuggestGasPrice", big.NewInt(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", big.NewInt(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "PendingNonceAt", uint64(1), nil) - p.ApplyMethodReturn(ðclient.Client{}, "EstimateGas", uint64(1), nil) - p.ApplyFuncReturn(ethtypes.SignTx, nil, nil) - p.ApplyMethodReturn(ðclient.Client{}, "SendTransaction", nil) - p.ApplyMethodReturn(ðtypes.Transaction{}, "Hash", common.Hash{}) - - o, err := New(conf, "1", "", "") - r.NoError(err) - contract, ok := o.(*ethereumContract) - r.True(ok) - tx, err := contract.sendTX(ctx, nil) - r.NoError(err) - r.Equal(tx, "0x0000000000000000000000000000000000000000000000000000000000000000") - }) -} diff --git a/output/output.go b/output/output.go deleted file mode 100644 index 53ecab0d..00000000 --- a/output/output.go +++ /dev/null @@ -1,54 +0,0 @@ -package output - -import "github.com/iotexproject/w3bstream/task" - -type Type string - -const ( - Stdout Type = "stdout" - EthereumContract Type = "ethereumContract" - SolanaProgram Type = "solanaProgram" - Textile Type = "textile" -) - -type Config struct { - Type Type `json:"type"` - Ethereum EthereumConfig `json:"ethereum"` - Solana SolanaConfig `json:"solana"` - Textile TextileConfig `json:"textile"` -} - -type EthereumConfig struct { - ChainEndpoint string `json:"chainEndpoint"` - ContractAddress string `json:"contractAddress"` - ReceiverAddress string `json:"receiverAddress,omitempty"` - ContractMethod string `json:"contractMethod"` - ContractAbiJSON string `json:"contractAbiJSON"` -} - -type SolanaConfig struct { - ChainEndpoint string `json:"chainEndpoint"` - ProgramID string `json:"programID"` - StateAccountPK string `json:"stateAccountPK"` -} - -type TextileConfig struct { - VaultID string `json:"vaultID"` -} - -type Output interface { - Output(proverID uint64, task *task.Task, proof []byte) (string, error) -} - -func New(conf *Config, privateKeyECDSA, privateKeyED25519 string, contractWhitelist string) (Output, error) { - switch conf.Type { - case EthereumContract: - return newEthereum(conf.Ethereum, privateKeyECDSA, contractWhitelist) - case SolanaProgram: - return newSolanaProgram(conf.Solana, privateKeyED25519) - case Textile: - return newTextileDBAdapter(conf.Textile, privateKeyECDSA) - default: - return newStdout(), nil - } -} diff --git a/output/output_test.go b/output/output_test.go deleted file mode 100644 index 59133c19..00000000 --- a/output/output_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package output - -import ( - "testing" - - . "github.com/agiledragon/gomonkey/v2" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/stretchr/testify/require" -) - -func TestNew(t *testing.T) { - r := require.New(t) - - t.Run("Default", func(t *testing.T) { - c := &Config{} - o, err := New(c, "", "", "") - r.NoError(err) - _, ok := o.(*stdout) - r.True(ok) - }) - t.Run("Stdout", func(t *testing.T) { - c := &Config{ - Type: Stdout, - } - o, err := New(c, "", "", "") - r.NoError(err) - _, ok := o.(*stdout) - r.True(ok) - }) - t.Run("Ethereum", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - - p.ApplyFuncReturn(ethclient.Dial, ðclient.Client{}, nil) - p.ApplyMethodReturn(ðclient.Client{}, "ChainID", nil, nil) - - c := &Config{ - Type: EthereumContract, - Ethereum: EthereumConfig{ - ContractAbiJSON: `[{"constant":true,"inputs":[],"name":"getProof","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes","name":"_proof","type":"bytes"}],"name":"setProof","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]`, - ContractMethod: "getProof", - }, - } - o, err := New(c, "c47bbade736b0f82788aa6eaa06140cdf41a544707edef944299642e0d708cab", "", "") - r.NoError(err) - _, ok := o.(*ethereumContract) - r.True(ok) - }) - t.Run("Solana", func(t *testing.T) { - c := &Config{ - Type: SolanaProgram, - Solana: SolanaConfig{}, - } - o, err := New(c, "", "308edd7fca562182adbffaa59264a138d9e04f9f3adbda2c80ef1ca71b7dcfa4", "") - r.NoError(err) - _, ok := o.(*solanaProgram) - r.True(ok) - }) -} diff --git a/output/solana.go b/output/solana.go deleted file mode 100644 index 1ea29afd..00000000 --- a/output/solana.go +++ /dev/null @@ -1,110 +0,0 @@ -package output - -import ( - "context" - "crypto/ed25519" - "log/slog" - - "github.com/blocto/solana-go-sdk/client" - solcommon "github.com/blocto/solana-go-sdk/common" - soltypes "github.com/blocto/solana-go-sdk/types" - "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" - - "github.com/iotexproject/w3bstream/task" -) - -type solanaProgram struct { - endpoint string - programID string - secretKey string - stateAccountPK string -} - -func (e *solanaProgram) Output(proverID uint64, task *task.Task, proof []byte) (string, error) { - slog.Debug("outputting to solana program", "chain endpoint", e.endpoint) - ins := e.packInstructions(proof) - txHash, err := e.sendTX(ins) - if err != nil { - return "", err - } - - return txHash, nil -} - -func (e *solanaProgram) sendTX(ins []soltypes.Instruction) (string, error) { - cli := client.NewClient(e.endpoint) - b := common.FromHex(e.secretKey) - pk := ed25519.PrivateKey(b) - account := soltypes.Account{ - PublicKey: solcommon.PublicKeyFromBytes(pk.Public().(ed25519.PublicKey)), - PrivateKey: pk, - } - if len(ins) == 0 { - return "", errors.New("missing instruction data") - } - - resp, err := cli.GetLatestBlockhash(context.Background()) - if err != nil { - return "", errors.Wrap(err, "failed to get solana latest block hash") - } - tx, err := soltypes.NewTransaction(soltypes.NewTransactionParam{ - Message: soltypes.NewMessage(soltypes.NewMessageParam{ - FeePayer: account.PublicKey, - RecentBlockhash: resp.Blockhash, - Instructions: ins, - }), - Signers: []soltypes.Account{account}, - }) - if err != nil { - return "", errors.Wrap(err, "failed to build solana raw tx") - } - - hash, err := cli.SendTransaction(context.Background(), tx) - if err != nil { - return "", errors.Wrap(err, "failed to send solana tx") - } - return hash, nil -} - -// encodeData encodes the proof into the data field of the instruction -// the first byte is the instruction, which is 0 for now; -// the rest is the proof data. -// e.g. assume proof is [0x01, 0x02, 0x03], then the encoded data is [0x00, 0x01, 0x02, 0x03] -func (e *solanaProgram) encodeData(proof []byte) []byte { - data := []byte{} - data = append(data, byte(0)) // 0 means submit proof - data = append(data, proof...) - return data -} - -func (e *solanaProgram) packInstructions(proof []byte) []soltypes.Instruction { - accounts := []soltypes.AccountMeta{} - if e.stateAccountPK != "" { - accounts = append(accounts, soltypes.AccountMeta{ - PubKey: solcommon.PublicKeyFromString(e.stateAccountPK), - IsSigner: false, - IsWritable: true, - }) - } - - return []soltypes.Instruction{ - { - ProgramID: solcommon.PublicKeyFromString(e.programID), - Accounts: accounts, - Data: e.encodeData(proof), - }, - } -} - -func newSolanaProgram(conf SolanaConfig, secretKey string) (*solanaProgram, error) { - if secretKey == "" { - return nil, errors.New("secret key is empty") - } - return &solanaProgram{ - endpoint: conf.ChainEndpoint, - programID: conf.ProgramID, - secretKey: secretKey, - stateAccountPK: conf.StateAccountPK, - }, nil -} diff --git a/output/solana_test.go b/output/solana_test.go deleted file mode 100644 index c140b435..00000000 --- a/output/solana_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package output - -import ( - "testing" - - . "github.com/agiledragon/gomonkey/v2" - "github.com/blocto/solana-go-sdk/client" - soltypes "github.com/blocto/solana-go-sdk/types" - "github.com/pkg/errors" - "github.com/stretchr/testify/require" - - "github.com/iotexproject/w3bstream/task" -) - -func patchSolanaProgramSendTX(p *Patches, txhash string, err error) *Patches { - return p.ApplyPrivateMethod(&solanaProgram{}, "sendTX", func(*solanaProgram, []soltypes.Instruction) (string, error) { - return txhash, err - }) -} - -func Test_solanaProgram_Output(t *testing.T) { - r := require.New(t) - e1 := &solanaProgram{stateAccountPK: ""} - e2 := &solanaProgram{stateAccountPK: "any"} - - t.Run("FailedToSendTX", func(t *testing.T) { - p := NewPatches() - defer p.Reset() - p = patchSolanaProgramSendTX(p, "", errors.New(t.Name())) - - txHash, err := e1.Output(uint64(0), &task.Task{}, nil) - r.Equal(txHash, "") - r.ErrorContains(err, t.Name()) - - txHash, err = e2.Output(uint64(0), &task.Task{}, nil) - r.Equal(txHash, "") - r.ErrorContains(err, t.Name()) - }) - - t.Run("Success", func(t *testing.T) { - txHashRet := "anyTxHash" - p := NewPatches() - defer p.Reset() - p = patchSolanaProgramSendTX(p, txHashRet, nil) - - txHash, err := e1.Output(uint64(0), &task.Task{}, nil) - r.Equal(txHash, txHashRet) - r.NoError(err) - - txHash, err = e2.Output(uint64(0), &task.Task{}, nil) - r.Equal(txHash, txHashRet) - r.NoError(err) - }) -} - -func Test_solanaProgram_sendTX(t *testing.T) { - r := require.New(t) - p := NewPatches() - defer p.Reset() - - contract := &solanaProgram{ - secretKey: "fd6ac80f1b9886a6d157cd8e71f842a63c52ebd237cf48fba03ae587e197d511f0b2439ae6da236d26f17f56c68f05d48513cd99b33143fa0b1aec7838ce4276", - } - ins := contract.packInstructions([]byte("proof")) - - t.Run("MissingInstructionData", func(t *testing.T) { - p = p.ApplyFuncReturn(client.NewClient, &client.Client{}) - _, err := contract.sendTX(nil) - r.EqualError(err, "missing instruction data") - }) - - t.Run("GetSolanaBlockFailed", func(t *testing.T) { - p = p.ApplyMethodReturn(&client.Client{}, "GetLatestBlockhash", nil, errors.New(t.Name())) - _, err := contract.sendTX(ins) - r.ErrorContains(err, t.Name()) - }) - p = p.ApplyMethodReturn(&client.Client{}, "GetLatestBlockhash", nil, nil) - - t.Run("BuildSolanaTxFailed", func(t *testing.T) { - p = p.ApplyFuncReturn(soltypes.NewTransaction, nil, errors.New(t.Name())) - _, err := contract.sendTX(ins) - r.ErrorContains(err, t.Name()) - }) - p = p.ApplyFuncReturn(soltypes.NewTransaction, nil, nil) - - t.Run("SendSolanaTxFailed", func(t *testing.T) { - p = p.ApplyMethodReturn(&client.Client{}, "SendTransaction", "", errors.New(t.Name())) - _, err := contract.sendTX(ins) - r.ErrorContains(err, t.Name()) - }) - - t.Run("SendSolanaTxSuccess", func(t *testing.T) { - p = p.ApplyMethodReturn(&client.Client{}, "SendTransaction", t.Name(), nil) - - hash, err := contract.sendTX(ins) - r.NoError(err) - r.Equal(t.Name(), hash) - }) -} diff --git a/output/stdout.go b/output/stdout.go deleted file mode 100644 index af354c81..00000000 --- a/output/stdout.go +++ /dev/null @@ -1,18 +0,0 @@ -package output - -import ( - "log/slog" - - "github.com/iotexproject/w3bstream/task" -) - -type stdout struct{} - -func (r *stdout) Output(proverID uint64, task *task.Task, proof []byte) (string, error) { - slog.Info("stdout", "proof", string(proof)) - return "", nil -} - -func newStdout() *stdout { - return &stdout{} -} diff --git a/output/stdout_test.go b/output/stdout_test.go deleted file mode 100644 index 8560cb8e..00000000 --- a/output/stdout_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package output - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/iotexproject/w3bstream/task" -) - -func Test_stdout_Output(t *testing.T) { - r := require.New(t) - o := &stdout{} - _, err := o.Output(uint64(0), &task.Task{}, []byte("any")) - r.NoError(err) -} diff --git a/output/testdata/testABI_dataSnarkInputOnlyMethod.json b/output/testdata/testABI_dataSnarkInputOnlyMethod.json deleted file mode 100644 index acd6cdbc..00000000 --- a/output/testdata/testABI_dataSnarkInputOnlyMethod.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "bytes", - "name": "_data_snark", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "data_snark", - "type": "bytes" - } - ], - "name": "testMethod", - "outputs": [], - "type": "function" - } -] \ No newline at end of file diff --git a/output/testdata/testABI_otherInputOnlyMethod.json b/output/testdata/testABI_otherInputOnlyMethod.json deleted file mode 100644 index 168ffb32..00000000 --- a/output/testdata/testABI_otherInputOnlyMethod.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "string", - "name": "other", - "type": "string", - "indexed": true - } - ], - "name": "testMethod", - "outputs": [], - "type": "function", - "stateMutability": "nonpayable" - } -] \ No newline at end of file diff --git a/output/testdata/testABI_otherInputOnlyMethod_address.json b/output/testdata/testABI_otherInputOnlyMethod_address.json deleted file mode 100644 index 9cc88dbf..00000000 --- a/output/testdata/testABI_otherInputOnlyMethod_address.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "other", - "type": "address", - "indexed": true - } - ], - "name": "testMethod", - "outputs": [], - "type": "function", - "stateMutability": "nonpayable" - } -] \ No newline at end of file diff --git a/output/testdata/testABI_otherInputOnlyMethod_uint256.json b/output/testdata/testABI_otherInputOnlyMethod_uint256.json deleted file mode 100644 index 90ddaf38..00000000 --- a/output/testdata/testABI_otherInputOnlyMethod_uint256.json +++ /dev/null @@ -1,16 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "uint256", - "name": "other", - "type": "uint256", - "indexed": true - } - ], - "name": "testMethod", - "outputs": [], - "type": "function", - "stateMutability": "nonpayable" - } -] \ No newline at end of file diff --git a/output/testdata/testABI_projectInputOnlyMethod.json b/output/testdata/testABI_projectInputOnlyMethod.json deleted file mode 100644 index 9fa7261c..00000000 --- a/output/testdata/testABI_projectInputOnlyMethod.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "uint256", - "name": "_projectId", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "projectId", - "type": "uint256" - } - ], - "name": "testMethod", - "outputs": [], - "type": "function" - } -] \ No newline at end of file diff --git a/output/testdata/testABI_proofInputOnlyMethod.json b/output/testdata/testABI_proofInputOnlyMethod.json deleted file mode 100644 index 62440494..00000000 --- a/output/testdata/testABI_proofInputOnlyMethod.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "bytes", - "name": "_proof", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "proof", - "type": "bytes" - } - ], - "name": "testMethod", - "outputs": [], - "type": "function" - } -] \ No newline at end of file diff --git a/output/testdata/testABI_receiverInputOnlyMethod.json b/output/testdata/testABI_receiverInputOnlyMethod.json deleted file mode 100644 index b8ed1d91..00000000 --- a/output/testdata/testABI_receiverInputOnlyMethod.json +++ /dev/null @@ -1,19 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "_receiver", - "type": "address" - }, - { - "internalType": "address", - "name": "receiver", - "type": "address" - } - ], - "name": "testMethod", - "outputs": [], - "type": "function" - } -] \ No newline at end of file diff --git a/output/textile.go b/output/textile.go deleted file mode 100644 index 53374aee..00000000 --- a/output/textile.go +++ /dev/null @@ -1,134 +0,0 @@ -package output - -import ( - "bytes" - "crypto/ecdsa" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "log/slog" - "net/http" - "strconv" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/pkg/errors" - "github.com/tablelandnetwork/basin-cli/pkg/signing" - "github.com/tidwall/gjson" - - "github.com/iotexproject/w3bstream/task" -) - -type textileDB struct { - endpoint string - secretKey *ecdsa.PrivateKey -} - -func (t *textileDB) Output(proverID uint64, task *task.Task, proof []byte) (string, error) { - slog.Debug("outputting to textileDB", "chain endpoint", t.endpoint) - encodedData, err := t.packData(proof) - if err != nil { - return "", err - } - txHash, err := t.write(encodedData) - if err != nil { - return "", err - } - return txHash, nil -} - -func (t *textileDB) packData(proof []byte) ([]byte, error) { - proof, err := hex.DecodeString(string(proof)) - if err != nil { - return nil, errors.Wrap(err, "failed to decode proof") - } - - var ( - result string - values []gjson.Result - ) - - if valueJournal := gjson.GetBytes(proof, "Stark.journal.bytes"); valueJournal.Exists() { - values = valueJournal.Array() - } else if valueJournal = gjson.GetBytes(proof, "Snark.journal"); valueJournal.Exists() { - values = valueJournal.Array() - } else { - return nil, errors.New("proof does not contain journal") - } - - // get result from proof - for _, value := range values { - result += fmt.Sprint(value.Int()) - } - - data := map[string]string{ - "result": result, - "proof": string(proof), - } - jsonData, err := json.Marshal(data) - if err != nil { - return nil, errors.Wrap(err, "marshal pack data error") - } - - return jsonData, nil -} - -func (t *textileDB) write(data []byte) (string, error) { - signatureBytes, err := signing.NewSigner(t.secretKey).SignBytes(data) - if err != nil { - return "", errors.Wrap(err, "failed to sign data") - } - - txHash := hex.EncodeToString(signatureBytes) - - url := fmt.Sprintf("%s?timestamp=%s&signature=%s", - t.endpoint, - strconv.FormatInt(time.Now().Unix(), 10), - txHash) - err = writeTextileEvent(url, data) - if err != nil { - return "", err - } - return txHash, err -} - -// writeTextileEvent writes a file to a vault via the Basin API. -func writeTextileEvent(url string, fileData []byte) error { - req, err := http.NewRequest("POST", url, bytes.NewReader(fileData)) - if err != nil { - return errors.Wrap(err, "failed to create request") - } - - hn := sha256.New() - hr := hn.Sum(fileData) - req.Header.Set("filename", string(hr)[0:7]) - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return errors.Wrap(err, "failed to send request") - } - defer resp.Body.Close() - - responseBody, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - - slog.Debug("Write event", "response", string(responseBody)) - return nil -} - -// TODO: refactor textile with a KV database adapter -func newTextileDBAdapter(conf TextileConfig, secretKey string) (*textileDB, error) { - if secretKey == "" { - return nil, errors.New("secret key is empty") - } - return &textileDB{ - endpoint: fmt.Sprintf("https://basin.tableland.xyz/vaults/%s/events", conf.VaultID), - secretKey: crypto.ToECDSAUnsafe(common.FromHex(secretKey)), - }, nil -} diff --git a/project/project.go b/project/project.go index ffcb58fb..5cee75fd 100644 --- a/project/project.go +++ b/project/project.go @@ -11,7 +11,6 @@ import ( "github.com/mailru/easyjson" "github.com/pkg/errors" - "github.com/iotexproject/w3bstream/output" "github.com/iotexproject/w3bstream/util/ipfs" ) @@ -21,10 +20,8 @@ var ( ) type Project struct { - DatasourceURI string `json:"datasourceURI,omitempty"` - DatasourcePubKey string `json:"datasourcePublicKey,omitempty"` - DefaultVersion string `json:"defaultVersion"` - Versions []*Config `json:"versions"` + DefaultVersion string `json:"defaultVersion"` + Versions []*Config `json:"versions"` } type Meta struct { @@ -39,11 +36,10 @@ type Attribute struct { } type Config struct { - Version string `json:"version"` - VMTypeID uint64 `json:"vmTypeID"` - Output output.Config `json:"output"` - CodeExpParam string `json:"codeExpParam,omitempty"` - Code string `json:"code"` + Version string `json:"version"` + VMTypeID uint64 `json:"vmTypeID"` + CodeExpParam string `json:"codeExpParam,omitempty"` + Code string `json:"code"` } func (p *Project) Config(version string) (*Config, error) { diff --git a/project/project_easyjson.go b/project/project_easyjson.go index b020b77f..f4fd2023 100644 --- a/project/project_easyjson.go +++ b/project/project_easyjson.go @@ -4,7 +4,6 @@ package project import ( json "encoding/json" - output "github.com/iotexproject/w3bstream/output" easyjson "github.com/mailru/easyjson" jlexer "github.com/mailru/easyjson/jlexer" jwriter "github.com/mailru/easyjson/jwriter" @@ -37,10 +36,6 @@ func easyjson7b166cadDecodeGithubComIotexprojectW3bstreamProject(in *jlexer.Lexe continue } switch key { - case "datasourceURI": - out.DatasourceURI = string(in.String()) - case "datasourcePublicKey": - out.DatasourcePubKey = string(in.String()) case "defaultVersion": out.DefaultVersion = string(in.String()) case "versions": @@ -88,30 +83,9 @@ func easyjson7b166cadEncodeGithubComIotexprojectW3bstreamProject(out *jwriter.Wr out.RawByte('{') first := true _ = first - if in.DatasourceURI != "" { - const prefix string = ",\"datasourceURI\":" - first = false - out.RawString(prefix[1:]) - out.String(string(in.DatasourceURI)) - } - if in.DatasourcePubKey != "" { - const prefix string = ",\"datasourcePublicKey\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } - out.String(string(in.DatasourcePubKey)) - } { const prefix string = ",\"defaultVersion\":" - if first { - first = false - out.RawString(prefix[1:]) - } else { - out.RawString(prefix) - } + out.RawString(prefix[1:]) out.String(string(in.DefaultVersion)) } { @@ -267,8 +241,6 @@ func easyjson7b166cadDecodeGithubComIotexprojectW3bstreamProject2(in *jlexer.Lex out.Version = string(in.String()) case "vmTypeID": out.VMTypeID = uint64(in.Uint64()) - case "output": - easyjson7b166cadDecodeGithubComIotexprojectW3bstreamOutput(in, &out.Output) case "codeExpParam": out.CodeExpParam = string(in.String()) case "code": @@ -297,11 +269,6 @@ func easyjson7b166cadEncodeGithubComIotexprojectW3bstreamProject2(out *jwriter.W out.RawString(prefix) out.Uint64(uint64(in.VMTypeID)) } - { - const prefix string = ",\"output\":" - out.RawString(prefix) - easyjson7b166cadEncodeGithubComIotexprojectW3bstreamOutput(out, in.Output) - } if in.CodeExpParam != "" { const prefix string = ",\"codeExpParam\":" out.RawString(prefix) @@ -338,237 +305,6 @@ func (v *Config) UnmarshalJSON(data []byte) error { func (v *Config) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjson7b166cadDecodeGithubComIotexprojectW3bstreamProject2(l, v) } -func easyjson7b166cadDecodeGithubComIotexprojectW3bstreamOutput(in *jlexer.Lexer, out *output.Config) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeFieldName(false) - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "type": - out.Type = output.Type(in.String()) - case "ethereum": - easyjson7b166cadDecodeGithubComIotexprojectW3bstreamOutput1(in, &out.Ethereum) - case "solana": - easyjson7b166cadDecodeGithubComIotexprojectW3bstreamOutput2(in, &out.Solana) - case "textile": - easyjson7b166cadDecodeGithubComIotexprojectW3bstreamOutput3(in, &out.Textile) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjson7b166cadEncodeGithubComIotexprojectW3bstreamOutput(out *jwriter.Writer, in output.Config) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"type\":" - out.RawString(prefix[1:]) - out.String(string(in.Type)) - } - { - const prefix string = ",\"ethereum\":" - out.RawString(prefix) - easyjson7b166cadEncodeGithubComIotexprojectW3bstreamOutput1(out, in.Ethereum) - } - { - const prefix string = ",\"solana\":" - out.RawString(prefix) - easyjson7b166cadEncodeGithubComIotexprojectW3bstreamOutput2(out, in.Solana) - } - { - const prefix string = ",\"textile\":" - out.RawString(prefix) - easyjson7b166cadEncodeGithubComIotexprojectW3bstreamOutput3(out, in.Textile) - } - out.RawByte('}') -} -func easyjson7b166cadDecodeGithubComIotexprojectW3bstreamOutput3(in *jlexer.Lexer, out *output.TextileConfig) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeFieldName(false) - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "vaultID": - out.VaultID = string(in.String()) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjson7b166cadEncodeGithubComIotexprojectW3bstreamOutput3(out *jwriter.Writer, in output.TextileConfig) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"vaultID\":" - out.RawString(prefix[1:]) - out.String(string(in.VaultID)) - } - out.RawByte('}') -} -func easyjson7b166cadDecodeGithubComIotexprojectW3bstreamOutput2(in *jlexer.Lexer, out *output.SolanaConfig) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeFieldName(false) - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "chainEndpoint": - out.ChainEndpoint = string(in.String()) - case "programID": - out.ProgramID = string(in.String()) - case "stateAccountPK": - out.StateAccountPK = string(in.String()) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjson7b166cadEncodeGithubComIotexprojectW3bstreamOutput2(out *jwriter.Writer, in output.SolanaConfig) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"chainEndpoint\":" - out.RawString(prefix[1:]) - out.String(string(in.ChainEndpoint)) - } - { - const prefix string = ",\"programID\":" - out.RawString(prefix) - out.String(string(in.ProgramID)) - } - { - const prefix string = ",\"stateAccountPK\":" - out.RawString(prefix) - out.String(string(in.StateAccountPK)) - } - out.RawByte('}') -} -func easyjson7b166cadDecodeGithubComIotexprojectW3bstreamOutput1(in *jlexer.Lexer, out *output.EthereumConfig) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeFieldName(false) - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "chainEndpoint": - out.ChainEndpoint = string(in.String()) - case "contractAddress": - out.ContractAddress = string(in.String()) - case "receiverAddress": - out.ReceiverAddress = string(in.String()) - case "contractMethod": - out.ContractMethod = string(in.String()) - case "contractAbiJSON": - out.ContractAbiJSON = string(in.String()) - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjson7b166cadEncodeGithubComIotexprojectW3bstreamOutput1(out *jwriter.Writer, in output.EthereumConfig) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"chainEndpoint\":" - out.RawString(prefix[1:]) - out.String(string(in.ChainEndpoint)) - } - { - const prefix string = ",\"contractAddress\":" - out.RawString(prefix) - out.String(string(in.ContractAddress)) - } - if in.ReceiverAddress != "" { - const prefix string = ",\"receiverAddress\":" - out.RawString(prefix) - out.String(string(in.ReceiverAddress)) - } - { - const prefix string = ",\"contractMethod\":" - out.RawString(prefix) - out.String(string(in.ContractMethod)) - } - { - const prefix string = ",\"contractAbiJSON\":" - out.RawString(prefix) - out.String(string(in.ContractAbiJSON)) - } - out.RawByte('}') -} func easyjson7b166cadDecodeGithubComIotexprojectW3bstreamProject3(in *jlexer.Lexer, out *Attribute) { isTopLevel := in.IsStart() if in.IsNull() {