diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9b31abf --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +IPFS_URL="" +SUPPORTED_RPC="" +SUPPORTED_STATE_CONTRACTS="80001=0x134B1BE34911E39A8397ec6289782989729807a4" +SUPPORTED_ISSUERS="" +ISSUERS_BASIC_AUTH="" diff --git a/Dockerfile b/Dockerfile index 84a0d03..e8b9b8f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,10 +14,10 @@ RUN apk add --no-cache libstdc++ gcompat libgomp WORKDIR /app -COPY ./circuits circuits +COPY ./keys keys COPY --from=base /build/refresh-service refresh-service COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -ENV CIRCUITS_FOLDER_PATH=/app/circuits +ENV CIRCUITS_FOLDER_PATH=/app/keys ENTRYPOINT ["/app/refresh-service"] diff --git a/README.md b/README.md index 81d2d06..eaa9d49 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,14 @@ It is **important to note** that the refresh service imposes a constraint on non To run this service, users should manage two configurations: one in a `.env` file and another in `config.yaml`. `.env` configuration is used for configure the server, `config.yaml` configuration is used for configure HTTP data provider. 1. `.env` file: ``` - SUPPORTED_ISSUERS - A list of supported issuers in the format `issuerDID:issuerNodeURL`. You can also use `*` to set a default node. + SUPPORTED_ISSUERS - A list of supported issuers in the format `issuerDID=issuerNodeURL`. You can also use `*` to set a default node. IPFS_URL - The URL of the IPFS node. SERVER_PORT - The server port. The default is 8002. HTTP_CONFIG_PATH - The path to the HTTP configuration. - SUPPORTED_RPC - Supported RPC in the format `chainID:URL`. - SUPPORTED_STATE_CONTRACTS - Supported state contracts in the format `chainID:contractAddress`. + SUPPORTED_RPC - Supported RPC in the format `chainID=URL`. + SUPPORTED_STATE_CONTRACTS - Supported state contracts in the format `chainID=contractAddress`. CIRCUITS_FOLDER_PATH - The path to the circuits folder. + ISSUERS_BASIC_AUTH - Basic auth for issuer int the format `issuerDID=user:password`. You can also use `*` to set the basic auth for all issuers. ``` 2. `config.yaml` for configure HTTP request to data providers: Example: diff --git a/go.mod b/go.mod index bd7bf9f..b86d5c3 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,9 @@ require ( github.com/google/uuid v1.3.0 github.com/iden3/contracts-abi/state/go/abi v1.0.1 github.com/iden3/go-circuits/v2 v2.0.0 - github.com/iden3/go-iden3-core/v2 v2.0.0 + github.com/iden3/go-iden3-core/v2 v2.0.3 github.com/iden3/go-jwz/v2 v2.0.0 - github.com/iden3/go-schema-processor/v2 v2.1.0 + github.com/iden3/go-schema-processor/v2 v2.2.0 github.com/iden3/iden3comm/v2 v2.0.1-0.20231030214854-7a0511d0e7cc github.com/ipfs/go-ipfs-api v0.6.0 github.com/kelseyhightower/envconfig v1.4.0 diff --git a/go.sum b/go.sum index 4c6d1de..b3b3da0 100644 --- a/go.sum +++ b/go.sum @@ -85,6 +85,8 @@ github.com/iden3/go-circuits/v2 v2.0.0 h1:Bw0mpsqeip06d6I2ktgfhTVB7Jk9mSHi8myHZW github.com/iden3/go-circuits/v2 v2.0.0/go.mod h1:VIFIp51+IH0hOzjnKhb84bCeyq7hq76zX/C14ua6zh4= github.com/iden3/go-iden3-core/v2 v2.0.0 h1:sQEuuq3RLfyYSY8qPiqxQ6YBpGbiAwepHJD/vjf1adA= github.com/iden3/go-iden3-core/v2 v2.0.0/go.mod h1:L9PxhWPvoS9qTb3inEkZBm1RpjHBt+VTwvxssdzbAdw= +github.com/iden3/go-iden3-core/v2 v2.0.3 h1:ce9Jbw10zDsinWXFc05SiK2Hof/wu4zV4/ai5gQy29k= +github.com/iden3/go-iden3-core/v2 v2.0.3/go.mod h1:L9PxhWPvoS9qTb3inEkZBm1RpjHBt+VTwvxssdzbAdw= github.com/iden3/go-iden3-crypto v0.0.15 h1:4MJYlrot1l31Fzlo2sF56u7EVFeHHJkxGXXZCtESgK4= github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= github.com/iden3/go-jwz/v2 v2.0.0 h1:VsU2PrmcchPMx/V0IhamMZRNjiQYZoyJopO8K8uSZOY= @@ -103,6 +105,8 @@ github.com/iden3/go-rapidsnark/witness/wazero v0.0.0-20230524142950-0986cf057d4e github.com/iden3/go-rapidsnark/witness/wazero v0.0.0-20230524142950-0986cf057d4e/go.mod h1:UEBifEzw62T6VzIHJeHuUgeLg2U/J9ttf7hOwQEqnYk= github.com/iden3/go-schema-processor/v2 v2.1.0 h1:8/fA7IVkyVmpbJij9Ar0X2zWPQJMlCaaPP4hfTZqbqU= github.com/iden3/go-schema-processor/v2 v2.1.0/go.mod h1:EogHwnFnxQKOGRVme6upCcisoAQeEIl+HuRAGa7w3+w= +github.com/iden3/go-schema-processor/v2 v2.2.0 h1:sYPqLs72pEWXIfF0/MOv9AFm3+IGutRM3yClWRrjheg= +github.com/iden3/go-schema-processor/v2 v2.2.0/go.mod h1:Ovsrk0839NZgHtoW4hVLAmHoOsHAQZuVNUXd7sIOkLQ= github.com/iden3/iden3comm/v2 v2.0.1-0.20231030214854-7a0511d0e7cc h1:VciWdH3N9hufuIn6w/SOys8+Bzjy8LD0l1f5aN2sghE= github.com/iden3/iden3comm/v2 v2.0.1-0.20231030214854-7a0511d0e7cc/go.mod h1:wrXoxi8eoQSLopatRW5+hYF9lDRvzGL2As9ZE88q/kA= github.com/ipfs/boxo v0.8.0 h1:UdjAJmHzQHo/j3g3b1bAcAXCj/GM6iTwvSlBDvPBNBs= diff --git a/keys/authV2.json b/keys/authV2.json new file mode 100644 index 0000000..b229c43 --- /dev/null +++ b/keys/authV2.json @@ -0,0 +1,104 @@ +{ + "protocol": "groth16", + "curve": "bn128", + "nPublic": 3, + "vk_alpha_1": [ + "20491192805390485299153009773594534940189261866228447918068658471970481763042", + "9383485363053290200918347156157836566562967994039712273449902621266178545958", + "1" + ], + "vk_beta_2": [ + [ + "6375614351688725206403948262868962793625744043794305715222011528459656738731", + "4252822878758300859123897981450591353533073413197771768651442665752259397132" + ], + [ + "10505242626370262277552901082094356697409835680220590971873171140371331206856", + "21847035105528745403288232691147584728191162732299865338377159692350059136679" + ], + [ + "1", + "0" + ] + ], + "vk_gamma_2": [ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634" + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531" + ], + [ + "1", + "0" + ] + ], + "vk_delta_2": [ + [ + "15934125614912710821614323121670433574627734468332981610453472911976383177228", + "13386788725021602198567425385006899728203544659933593917276469726154154017730" + ], + [ + "8759505107016263108323717548646403750748432711908544803866765373342463765424", + "13205305607413475134301212820100793870092003365382735436692046794406857938024" + ], + [ + "1", + "0" + ] + ], + "vk_alphabeta_12": [ + [ + [ + "2029413683389138792403550203267699914886160938906632433982220835551125967885", + "21072700047562757817161031222997517981543347628379360635925549008442030252106" + ], + [ + "5940354580057074848093997050200682056184807770593307860589430076672439820312", + "12156638873931618554171829126792193045421052652279363021382169897324752428276" + ], + [ + "7898200236362823042373859371574133993780991612861777490112507062703164551277", + "7074218545237549455313236346927434013100842096812539264420499035217050630853" + ] + ], + [ + [ + "7077479683546002997211712695946002074877511277312570035766170199895071832130", + "10093483419865920389913245021038182291233451549023025229112148274109565435465" + ], + [ + "4595479056700221319381530156280926371456704509942304414423590385166031118820", + "19831328484489333784475432780421641293929726139240675179672856274388269393268" + ], + [ + "11934129596455521040620786944827826205713621633706285934057045369193958244500", + "8037395052364110730298837004334506829870972346962140206007064471173334027475" + ] + ] + ], + "IC": [ + [ + "12385314984359904314257455036963499193805822249900169493212773820637861017270", + "13455871848617958073752171682190449799364399689372987044617812281838570851280", + "1" + ], + [ + "1493564767784757620464057507283285365409721187164502463730502309417194080296", + "6377944811748764752279954590131952700069491229367911408873461121555475171995", + "1" + ], + [ + "17810471156883173964067651564103955395454521925125801510057769541384109536787", + "5548963437503981062668882632052452068705295424483999545932010198708798592260", + "1" + ], + [ + "13853274336731202523728826661915506795333516652854674163618978302237601632434", + "15420320918214290109713867361085955935385737854012308761626909938871786338011", + "1" + ] + ] +} \ No newline at end of file diff --git a/main.go b/main.go index 9752784..e5add41 100644 --- a/main.go +++ b/main.go @@ -64,7 +64,7 @@ func main() { packageManager, err := packagemanager.NewPackageManager( cfg.SupportedRPC, cfg.SupportedStateContracts, - cfg.CircuitsFolderPath, + packagemanager.WithVerificationKeyPath(cfg.CircuitsFolderPath), ) if err != nil { log.Fatalf("failed init package manager: %v", err) diff --git a/packagemanager/packagemanager.go b/packagemanager/packagemanager.go index 1259ef2..9cfd8fe 100644 --- a/packagemanager/packagemanager.go +++ b/packagemanager/packagemanager.go @@ -20,94 +20,107 @@ import ( "github.com/pkg/errors" ) -var chainIDs = map[string]int{ - "eth": 1, - "eth:main": 1, - "eth:goerli": 5, - "eth:sepolia": 11155111, - "polygon": 137, - "polygon:main": 137, - "polygon:mumbai": 80001, - "zkevm": 1101, - "zkevm:main": 1101, - "zkevm:test": 1442, -} - type state struct { - contracts map[int]*abi.State + contracts map[int]*abi.State + globalStateValidDuration time.Duration } func (s *state) verify(_ circuits.CircuitID, pubsignals []string) error { bytePubsig, err := json.Marshal(pubsignals) if err != nil { - return err + return errors.Errorf("error marshaling pubsignals: %v", err) } authPubSignals := circuits.AuthV2PubSignals{} err = authPubSignals.PubSignalsUnmarshal(bytePubsig) if err != nil { - return err - } - - did, err := core.ParseDIDFromID(*authPubSignals.UserID) - if err != nil { - return err + return errors.Errorf("error unmarshaling pubsignals: %v", err) } - id, err := core.IDFromDID(*did) + userDID, err := core.ParseDIDFromID(*authPubSignals.UserID) if err != nil { - return errors.WithStack(err) + return errors.Errorf("error convertign userID '%s' to userDID: %v", + authPubSignals.UserID.String(), err) } - blockchain, err := core.BlockchainFromID(id) + chainID, err := core.ChainIDfromDID(*userDID) if err != nil { - return errors.WithStack(err) + return errors.Errorf("error extracting chainID from userDID '%s': %v", + userDID.String(), err) } - networkID, err := core.NetworkIDFromID(id) - if err != nil { - return errors.WithStack(err) - } - - chainID := chainIDs[fmt.Sprintf("%s:%s", blockchain, networkID)] - contract, ok := s.contracts[chainID] + contract, ok := s.contracts[int(chainID)] if !ok { - return errors.Errorf("not supported blockchain %s", blockchain) + return errors.Errorf("not supported chainID '%d'", chainID) } globalState := authPubSignals.GISTRoot.BigInt() globalStateInfo, err := contract.GetGISTRootInfo(&bind.CallOpts{}, globalState) if err != nil { - return err + return errors.Errorf("error getting global state info by state '%s': %v", + globalState, err) } if (big.NewInt(0)).Cmp(globalStateInfo.CreatedAtTimestamp) == 0 { - return errors.Errorf("root %s doesn't exist in smart contract", globalState.String()) + return errors.Errorf("root %s doesn't exist in smart contract", + globalState.String()) } if globalState.Cmp(globalStateInfo.Root) != 0 { - return errors.Errorf("invalid global state info in the smart contract, expected root %s, got %s", globalState.String(), globalStateInfo.Root.String()) + return errors.Errorf("invalid global state info in the smart contract, expected root %s, got %s", + globalState.String(), globalStateInfo.Root.String()) } - if (big.NewInt(0)).Cmp(globalStateInfo.ReplacedByRoot) != 0 && time.Since(time.Unix(globalStateInfo.ReplacedAtTimestamp.Int64(), 0)) > time.Minute*15 { - return errors.Errorf("global state is too old, replaced timestamp is %v", globalStateInfo.ReplacedAtTimestamp.Int64()) + if (big.NewInt(0)).Cmp(globalStateInfo.ReplacedByRoot) != 0 && + time.Since(time.Unix(globalStateInfo.ReplacedAtTimestamp.Int64(), 0)) > s.globalStateValidDuration { + return errors.Errorf("global state is too old, replaced timestamp is %v", + globalStateInfo.ReplacedAtTimestamp.Int64()) } return nil } +type Options struct { + VerificationKeyPath string + GlobalStateValidDuration time.Duration +} + +type Option func(*Options) + +func WithVerificationKeyPath(path string) Option { + return func(o *Options) { + o.VerificationKeyPath = path + } +} + +func WithGlobalStateValidDuration(duration time.Duration) Option { + return func(o *Options) { + o.GlobalStateValidDuration = duration + } +} + func NewPackageManager( supportedRPC map[string]string, supportedStateContracts map[string]string, - circuitsFolderPath string, + opts ...Option, ) (*iden3comm.PackageManager, error) { - circuitsPath := fmt.Sprintf("%s/%s", circuitsFolderPath, "authV2") - verificationKey, err := os.ReadFile(fmt.Sprintf("%s/verification_key.json", circuitsPath)) + + options := &Options{ + VerificationKeyPath: "/keys", + GlobalStateValidDuration: time.Minute * 15, + } + for _, opt := range opts { + opt(options) + } + + authV2VerificationKeyPath := fmt.Sprintf("%s/authV2.json", options.VerificationKeyPath) + verificationKey, err := os.ReadFile(authV2VerificationKeyPath) if err != nil { return nil, errors.Errorf( - "issuer with the file verification_key.json by path '%s': %v", circuitsPath, err) + "issuer with the file verification_key.json by path '%s': %v", authV2VerificationKeyPath, err) } states := state{ - contracts: make(map[int]*abi.State, len(supportedStateContracts)), + contracts: make(map[int]*abi.State, len(supportedStateContracts)), + globalStateValidDuration: options.GlobalStateValidDuration, } for chainID, stateAddr := range supportedStateContracts { rpcURL, ok := supportedRPC[chainID] diff --git a/service/refresh.go b/service/refresh.go index 4be5184..c95ae3c 100644 --- a/service/refresh.go +++ b/service/refresh.go @@ -45,6 +45,7 @@ type credentialRequest struct { Expiration int64 `json:"expiration"` RefreshService *verifiable.RefreshService `json:"refreshService,omitempty"` RevNonce *uint64 `json:"revNonce,omitempty"` + DisplayMethod *verifiable.DisplayMethod `json:"displayMethod,omitempty"` } func (rs *RefreshService) Process( @@ -114,6 +115,7 @@ func (rs *RefreshService) Process( Expiration: time.Now().Add(flexibleHTTP.Settings.TimeExpiration).Unix(), RefreshService: credential.RefreshService, RevNonce: &revNonce, + DisplayMethod: credential.DisplayMethod, } refreshedID, err := rs.issuerService.CreateCredential(issuer, credentialRequest)