diff --git a/go.mod b/go.mod index 3923254..c691914 100644 --- a/go.mod +++ b/go.mod @@ -4,32 +4,47 @@ go 1.19 require ( github.com/ethereum/go-ethereum v1.12.2 + github.com/iden3/contracts-abi/onchain-credential-status-resolver/go/abi v0.0.0-20230911113809-c58b7e7a69b0 github.com/iden3/contracts-abi/rhs-storage/go/abi v0.0.0-20231006141557-7d13ef7e3c48 + github.com/iden3/contracts-abi/state/go/abi v0.0.0-20230405152923-4a25f6f1f0f4 + github.com/iden3/go-iden3-core/v2 v2.0.3 github.com/iden3/go-merkletree-sql/v2 v2.0.4 - github.com/stretchr/testify v1.8.2 + github.com/iden3/go-schema-processor/v2 v2.3.0 + github.com/jarcoal/httpmock v1.3.1 + github.com/pkg/errors v0.9.1 + github.com/stretchr/testify v1.8.4 ) require ( github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dchest/blake512 v1.0.0 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-stack/stack v1.8.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/gorilla/websocket v1.4.2 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/holiman/uint256 v1.2.3 // indirect + github.com/huin/goupnp v1.2.0 // indirect github.com/iden3/go-iden3-crypto v0.0.15 // indirect + github.com/klauspost/compress v1.16.5 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect + github.com/prometheus/client_model v0.4.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect - golang.org/x/crypto v0.9.0 // indirect + golang.org/x/crypto v0.12.0 // indirect golang.org/x/exp v0.0.0-20230810033253-352e893a4cad // indirect - golang.org/x/sys v0.9.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + golang.org/x/sys v0.15.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f724d3a..69f4890 100644 --- a/go.sum +++ b/go.sum @@ -17,15 +17,15 @@ github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/Yj github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dchest/blake512 v1.0.0 h1:oDFEQFIqFSeuA34xLtXZ/rWxCXdSjirjzPhey5EUvmA= +github.com/dchest/blake512 v1.0.0/go.mod h1:FV1x7xPPLWukZlpDpWQ88rF/SFwZ5qbskrzhLMB92JI= github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= github.com/ethereum/go-ethereum v1.12.2 h1:eGHJ4ij7oyVqUQn48LBz3B7pvQ8sV0wGJiIE6gDq/6Y= github.com/ethereum/go-ethereum v1.12.2/go.mod h1:1cRAEV+rp/xX0zraSCBnu9Py3HQ+geRMj3HdR+k0wfI= @@ -41,46 +41,70 @@ github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= -github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= +github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= +github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/iden3/contracts-abi/onchain-credential-status-resolver/go/abi v0.0.0-20230911113809-c58b7e7a69b0 h1:Fu1/tAINi9FzZ0nKoEVOGXWzL1l15tR1loJx5sQEQ8k= +github.com/iden3/contracts-abi/onchain-credential-status-resolver/go/abi v0.0.0-20230911113809-c58b7e7a69b0/go.mod h1:8fkd2xyUG/V7ovpvZRyD2LyK2zZ4ALbgf5vJGyhzKdg= github.com/iden3/contracts-abi/rhs-storage/go/abi v0.0.0-20231006141557-7d13ef7e3c48 h1:/g4rru+OM5WAhVNXEpowKH+vWZLGjgpk5+O8Stu4QBo= github.com/iden3/contracts-abi/rhs-storage/go/abi v0.0.0-20231006141557-7d13ef7e3c48/go.mod h1:kJmVPMk4HfWyl2kcta34aad/K4TAfCwB29xX9PsR7LQ= +github.com/iden3/contracts-abi/state/go/abi v0.0.0-20230405152923-4a25f6f1f0f4 h1:iPvYa/AhhGo3juoUFDm/fBE2CZKy4WfQu7JY90tRf9Q= +github.com/iden3/contracts-abi/state/go/abi v0.0.0-20230405152923-4a25f6f1f0f4/go.mod h1:TxgIrXCvxms3sbOdsy8kTvffUCIpEEifNy0fSXdkU4w= +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-merkletree-sql/v2 v2.0.4 h1:Dp089P3YNX1BE8+T1tKQHWTtnk84Y/Kr7ZAGTqwscoY= github.com/iden3/go-merkletree-sql/v2 v2.0.4/go.mod h1:kRhHKYpui5DUsry5RpveP6IC4XMe6iApdV9VChRYuEk= +github.com/iden3/go-schema-processor/v2 v2.2.1-0.20240126124145-b6f321093157 h1:bvZczBLCisoDGtWF8knmjlLLn7/VWEhfAHV0X6BXAHY= +github.com/iden3/go-schema-processor/v2 v2.2.1-0.20240126124145-b6f321093157/go.mod h1:BcHVDZyn8q8vUlL+XpOo7hpwXmEjxzO8ao1LkvFsM+k= +github.com/iden3/go-schema-processor/v2 v2.3.0 h1:86tnt1myHntcG+9pJ3I+0ycc6V59sITrsPSt0k7/DhU= +github.com/iden3/go-schema-processor/v2 v2.3.0/go.mod h1:BcHVDZyn8q8vUlL+XpOo7hpwXmEjxzO8ao1LkvFsM+k= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= -github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= +github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f h1:HlPa7RcxTCrva5izPfTEfvYecO7LTahgmMRD1Qp13xg= +github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f/go.mod h1:WZ501QQMbZZ+3pXFPhQKzNwS1+jls0oqov3uQ2WasLs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= +github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= @@ -89,13 +113,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= @@ -105,25 +124,29 @@ github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZF github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/urfave/cli/v2 v2.24.1 h1:/QYYr7g0EhwXEML8jO+8OYt5trPnLHS0p3mrgExJ5NU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20230810033253-352e893a4cad h1:g0bG7Z4uG+OgH2QDODnjp6ggkk1bJDsINcuWmJN1iJU= golang.org/x/exp v0.0.0-20230810033253-352e893a4cad/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= diff --git a/resolvers/onchain.go b/resolvers/onchain.go new file mode 100644 index 0000000..ca0d2ca --- /dev/null +++ b/resolvers/onchain.go @@ -0,0 +1,406 @@ +package resolvers + +import ( + "context" + "encoding/hex" + "fmt" + "math/big" + "net/url" + "strconv" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + onchainABI "github.com/iden3/contracts-abi/onchain-credential-status-resolver/go/abi" + "github.com/iden3/contracts-abi/state/go/abi" + core "github.com/iden3/go-iden3-core/v2" + "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/go-merkletree-sql/v2" + "github.com/iden3/go-schema-processor/v2/utils" + "github.com/iden3/go-schema-processor/v2/verifiable" + "github.com/pkg/errors" +) + +// OnChainResolver is a struct that allows to interact with the onchain contract and build the revocation status. +type OnChainResolver struct { + ethClients map[core.ChainID]*ethclient.Client + stateContractAddresses map[core.ChainID]common.Address +} + +// NewOnChainResolver returns new onChain resolver +func NewOnChainResolver(ethClients map[core.ChainID]*ethclient.Client, stateContractAddresses map[core.ChainID]common.Address) *OnChainResolver { + return &OnChainResolver{ + ethClients: ethClients, + stateContractAddresses: stateContractAddresses, + } +} + +// Resolve is a method to resolve a credential status from the blockchain. +func (r OnChainResolver) Resolve(ctx context.Context, + status verifiable.CredentialStatus) (out verifiable.RevocationStatus, err error) { + + if status.Type != verifiable.Iden3OnchainSparseMerkleTreeProof2023 { + return out, errors.New("invalid status type") + } + + issuerDID := verifiable.GetIssuerDID(ctx) + if issuerDID == nil { + return out, errors.New("issuer DID is not set in context") + } + + issuerID, err := core.IDFromDID(*issuerDID) + if err != nil { + return out, errors.WithMessage((err), "can't parse issuer DID") + } + + ethClient, err := getEthClientForDID(issuerDID, r.ethClients) + if err != nil { + return out, err + } + + stateAddr, err := getStateContractForDID(issuerDID, r.stateContractAddresses) + if err != nil { + return out, err + } + + onchainRevStatus, err := newOnchainRevStatusFromURI(status.ID, status.RevocationNonce) + if err != nil { + return out, err + } + + contractCaller, err := onchainABI.NewOnchainCredentialStatusResolverCaller(onchainRevStatus.contractAddress, ethClient) + if err != nil { + return out, err + } + + if onchainRevStatus.revNonce != status.RevocationNonce { + return out, fmt.Errorf( + "revocationNonce is not equal to the one "+ + "in OnChainCredentialStatus ID {%d} {%d}", + onchainRevStatus.revNonce, status.RevocationNonce) + } + + isStateContractHasID, err := stateContractHasID(ctx, stateAddr, ethClient, &issuerID) + if err != nil { + return out, err + } + + contractOpts := &bind.CallOpts{Context: ctx} + + var resp onchainABI.IOnchainCredentialStatusResolverCredentialStatus + if isStateContractHasID { + resp, err = contractCaller.GetRevocationStatus(contractOpts, issuerID.BigInt(), + onchainRevStatus.revNonce) + if err != nil { + msg := err.Error() + if isErrInvalidRootsLength(err) { + msg = "roots were not saved to identity tree store" + } + return out, fmt.Errorf( + "GetRevocationProof smart contract call [GetRevocationStatus]: %s", + msg) + } + } else { + if onchainRevStatus.genesisState == nil { + return out, errors.New( + "genesis state is not specified in OnChainCredentialStatus ID") + } + resp, err = contractCaller.GetRevocationStatusByIdAndState( + contractOpts, + issuerID.BigInt(), onchainRevStatus.genesisState, + onchainRevStatus.revNonce) + if err != nil { + return out, fmt.Errorf( + "GetRevocationProof smart contract call [GetRevocationStatusByIdAndState]: %s", + err.Error()) + } + } + + return toRevocationStatus(resp) +} + +func newOnchainRevStatusFromURI(statusID string, statusRevNonce uint64) (onChainRevStatus, error) { + var s onChainRevStatus + + uri, err := url.Parse(statusID) + if err != nil { + return s, errors.Wrapf(err, "OnChainCredentialStatus ID is not a valid URI") + } + + contract := uri.Query().Get("contractAddress") + if contract == "" { + return s, errors.New("OnChainCredentialStatus contract address is empty") + } + + contractParts := strings.Split(contract, ":") + if len(contractParts) != 2 { + return s, errors.Errorf( + "OnChainCredentialStatus contract address '%s' is not valid", contract) + } + + s.chainID, err = newChainIDFromString(contractParts[0]) + if err != nil { + return s, err + } + s.contractAddress = common.HexToAddress(contractParts[1]) + + queryRevNonceString := uri.Query().Get("revocationNonce") + if queryRevNonceString != "" { + queryRevNonce, err := strconv.ParseUint(queryRevNonceString, 10, 64) + if err != nil { + return s, errors.New("query revocationNonce is not a number in OnChainCredentialStatus ID") + } + if queryRevNonce != statusRevNonce { + return s, errors.New("query revocation nonce doesn't match with credential status nonce") + } + + } + + s.revNonce = statusRevNonce + + // state may be nil if params is absent in query + s.genesisState, err = newIntFromHexQueryParam(uri, "state") + if err != nil { + return s, err + } + + return s, nil +} + +func newChainIDFromString(in string) (core.ChainID, error) { + radix := 10 + if strings.HasPrefix(in, "0x") || strings.HasPrefix(in, "0X") { + radix = 16 + in = in[2:] + } + + var chainID core.ChainID + assertUnderlineTypeInt32(chainID) + i, err := strconv.ParseInt(in, radix, 32) + if err != nil { + return 0, fmt.Errorf("can't parse ChainID type: %w", err) + } + return core.ChainID(i), nil +} + +// newIntFromHexQueryParam search for query param `paramName`, parse it +// as hex string of LE bytes of *big.Int. Return nil if param is not found. +func newIntFromHexQueryParam(uri *url.URL, paramName string) (*big.Int, error) { + stateParam := uri.Query().Get(paramName) + if stateParam == "" { + return nil, nil + } + + stateParam = strings.TrimPrefix(stateParam, "0x") + stateBytes, err := hex.DecodeString(stateParam) + if err != nil { + return nil, err + } + + return newIntFromBytesLE(stateBytes), nil +} + +func newIntFromBytesLE(bs []byte) *big.Int { + return new(big.Int).SetBytes(utils.SwapEndianness(bs)) +} + +func stateContractHasID(ctx context.Context, stateAddr common.Address, ethClient *ethclient.Client, id *core.ID) (bool, error) { + + if idsInStateContract.has(*id) { + return true, nil + } + + _, err := lastStateFromContract(ctx, stateAddr, ethClient, id) + if errors.Is(err, errIdentityDoesNotExist) { + return false, nil + } else if err != nil { + return false, err + } + + idsInStateContract.add(*id) + return true, err +} + +type onChainRevStatus struct { + chainID core.ChainID + contractAddress common.Address + revNonce uint64 + genesisState *big.Int +} + +func isErrInvalidRootsLength(err error) bool { + if err == nil { + return false + } + return err.Error() == "execution reverted: Invalid roots length" +} + +var errIdentityDoesNotExist = errors.New("identity does not exist") + +func isErrIdentityDoesNotExist(err error) bool { + if err == nil { + return false + } + return err.Error() == "execution reverted: Identity does not exist" +} + +type syncedIDsSet struct { + sync.RWMutex + m map[core.ID]bool +} + +func (is *syncedIDsSet) has(id core.ID) bool { + is.RLock() + defer is.RUnlock() + return is.m[id] +} + +func (is *syncedIDsSet) add(id core.ID) { + is.Lock() + defer is.Unlock() + if is.m == nil { + is.m = make(map[core.ID]bool) + } + is.m[id] = true +} + +var idsInStateContract syncedIDsSet + +func lastStateFromContract(ctx context.Context, stateAddr common.Address, ethClient *ethclient.Client, + id *core.ID) (*merkletree.Hash, error) { + var zeroID core.ID + if id == nil || *id == zeroID { + return nil, errors.New("ID is empty") + } + + contractCaller, err := abi.NewStateCaller(stateAddr, ethClient) + if err != nil { + return nil, err + } + + opts := &bind.CallOpts{Context: ctx} + resp, err := contractCaller.GetStateInfoById(opts, id.BigInt()) + if isErrIdentityDoesNotExist(err) { + return nil, errIdentityDoesNotExist + } else if err != nil { + return nil, err + } + + if resp.State == nil { + return nil, errors.New("got empty state") + } + + return merkletree.NewHashFromBigInt(resp.State) +} + +func getEthClientForDID(did *w3c.DID, ethClients map[core.ChainID]*ethclient.Client) (*ethclient.Client, error) { + chainID, err := core.ChainIDfromDID(*did) + if err != nil { + return nil, err + } + + ethClient, ok := ethClients[chainID] + if !ok { + return nil, errors.Errorf("chain id is not registered for network '%d'", chainID) + } + return ethClient, nil +} + +func getStateContractForDID(did *w3c.DID, stateContracts map[core.ChainID]common.Address) (out common.Address, err error) { + chainID, err := core.ChainIDfromDID(*did) + if err != nil { + return out, err + } + + contractAddr, ok := stateContracts[chainID] + if !ok { + return out, errors.Errorf("chain id is not registered for network '%d'", chainID) + } + return contractAddr, nil +} + +func toRevocationStatus(status onchainABI.IOnchainCredentialStatusResolverCredentialStatus) (out verifiable.RevocationStatus, err error) { + var existence bool + var nodeAux *merkletree.NodeAux + + if status.Mtp.Existence { + existence = true + } else { + existence = false + if status.Mtp.AuxExistence { + nodeAux = &merkletree.NodeAux{} + nodeAux.Key, err = merkletree.NewHashFromBigInt(status.Mtp.AuxIndex) + if err != nil { + return out, errors.New("aux index is not a number") + } + nodeAux.Value, err = merkletree.NewHashFromBigInt(status.Mtp.AuxValue) + if err != nil { + return out, errors.New("aux value is not a number") + } + } + } + + depth := calculateDepth(status.Mtp.Siblings) + allSiblings := make([]*merkletree.Hash, depth) + for i := 0; i < depth; i++ { + sh, err := merkletree.NewHashFromBigInt(status.Mtp.Siblings[i]) + if err != nil { + return out, errors.New("sibling is not a number") + } + allSiblings[i] = sh + } + + proof, err := merkletree.NewProofFromData(existence, allSiblings, nodeAux) + if err != nil { + return out, errors.New("failed to create proof") + } + + state, err := merkletree.NewHashFromBigInt(status.Issuer.State) + if err != nil { + return out, errors.New("state is not a number") + } + + claimsRoot, err := merkletree.NewHashFromBigInt(status.Issuer.ClaimsTreeRoot) + if err != nil { + return out, errors.New("claims tree root is not a number") + } + + revocationRoot, err := merkletree.NewHashFromBigInt(status.Issuer.RevocationTreeRoot) + if err != nil { + return out, errors.New("revocation tree root is not a number") + } + + rootOfRoots, err := merkletree.NewHashFromBigInt(status.Issuer.RootOfRoots) + if err != nil { + return out, errors.New("root of roots tree root is not a number") + } + + stateHex := state.Hex() + claimsRootHex := claimsRoot.Hex() + revocationRootHex := revocationRoot.Hex() + rootOfRootsHex := rootOfRoots.Hex() + + return verifiable.RevocationStatus{ + MTP: *proof, + Issuer: verifiable.TreeState{ + State: &stateHex, + ClaimsTreeRoot: &claimsRootHex, + RevocationTreeRoot: &revocationRootHex, + RootOfRoots: &rootOfRootsHex, + }, + }, nil +} + +func calculateDepth(siblings []*big.Int) int { + for i := len(siblings) - 1; i >= 0; i-- { + if siblings[i].Cmp(big.NewInt(0)) != 0 { + return i + 1 + } + } + return 0 +} + +// function to fail a compilation if underlined type is not int32 +func assertUnderlineTypeInt32[T ~int32](_ T) {} diff --git a/resolvers/onchain_test.go b/resolvers/onchain_test.go new file mode 100644 index 0000000..7899505 --- /dev/null +++ b/resolvers/onchain_test.go @@ -0,0 +1,63 @@ +package resolvers + +import ( + "context" + "encoding/json" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + core "github.com/iden3/go-iden3-core/v2" + "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/go-schema-processor/v2/verifiable" + "github.com/jarcoal/httpmock" + "github.com/stretchr/testify/require" +) + +func TestOnChainResolver(t *testing.T) { + credStatusJSON := `{ + "id": "did:polygonid:polygon:mumbai:2qHjMLYdcnQ39SFXLhujJe35ZRm9X8zcqUo3nR7PJ9/credentialStatus?revocationNonce=2407271101&contractAddress=80001:0x76EB7216F2400aC18C842D8C76739F3B8E619DB9&state=325723250b520e12a42b26330144a8462b45245a498af755f1cb925580340c0f", + "revocationNonce": 2407271101, + "type": "Iden3OnchainSparseMerkleTreeProof2023" + }` + + var credStatus verifiable.CredentialStatus + err := json.Unmarshal([]byte(credStatusJSON), &credStatus) + require.NoError(t, err) + + issuerDID, err := w3c.ParseDID("did:polygonid:polygon:mumbai:2qHjMLYdcnQ39SFXLhujJe35ZRm9X8zcqUo3nR7PJ9") + require.NoError(t, err) + stateAddr := common.HexToAddress("0x134B1BE34911E39A8397ec6289782989729807a4") + client, err := ethclient.Dial("http://my-rpc/v2/1111") + require.NoError(t, err) + var ethClients map[core.ChainID]*ethclient.Client = make(map[core.ChainID]*ethclient.Client) + ethClients[80001] = client + + var stateAddresses map[core.ChainID]common.Address = make(map[core.ChainID]common.Address) + stateAddresses[80001] = stateAddr + + onChainResolver := NewOnChainResolver(ethClients, stateAddresses) + + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + httpmock.RegisterMatcherResponder("POST", "http://my-rpc/v2/1111", + httpmock.BodyContainsString(`{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"data":"0xb4bdea55000af701e50b7414a0d1e05669487bf90a4e61fa7e031a73070a2543f1731202","from":"0x0000000000000000000000000000000000000000","to":"0x134b1be34911e39a8397ec6289782989729807a4"},"latest"]}`), + httpmock.NewStringResponder(200, `{"jsonrpc":"2.0","id":1,"result":"0x000af701e50b7414a0d1e05669487bf90a4e61fa7e031a73070a2543f17312022f129fd9ea47a95fd13c5205e813ad32636abfb5e6d731dcf8c129d3cca880d800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065a79fd200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ac964c0000000000000000000000000000000000000000000000000000000000000000"}`)) + + httpmock.RegisterMatcherResponder("POST", "http://my-rpc/v2/1111", + httpmock.BodyContainsString(`{"jsonrpc":"2.0","id":2,"method":"eth_call","params":[{"data":"0x110c96a7000af701e50b7414a0d1e05669487bf90a4e61fa7e031a73070a2543f1731202000000000000000000000000000000000000000000000000000000008f7c0abd","from":"0x0000000000000000000000000000000000000000","to":"0x76eb7216f2400ac18c842d8c76739f3b8e619db9"},"latest"]}`), + httpmock.NewStringResponder(200, `{"jsonrpc":"2.0","id":2,"result":"0x00000000000000000000000000000000000000000000000000000000000000202f129fd9ea47a95fd13c5205e813ad32636abfb5e6d731dcf8c129d3cca880d81530b53742768d91fd4b17e9ebbd276270cf00885feee7d9a013722ead3f5cf900000000000000000000000000000000000000000000000000000000000000002e29026e7eda49f6cd839e02ca72fd0bba794804757f02007ef39dda84bfaa7f00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000008f7c0abd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"} + `)) + + ctx := verifiable.WithIssuerDID(context.Background(), issuerDID) + status, err := onChainResolver.Resolve(ctx, credStatus) + require.NoError(t, err) + require.NotNil(t, status) + require.Equal(t, *status.Issuer.State, "d880a8ccd329c1f8dc31d7e6b5bf6a6332ad13e805523cd15fa947ead99f122f") + require.Equal(t, *status.Issuer.RootOfRoots, "7faabf84da9df37e00027f75044879ba0bfd72ca029e83cdf649da7e6e02292e") + require.Equal(t, *status.Issuer.ClaimsTreeRoot, "f95c3fad2e7213a0d9e7ee5f8800cf706227bdebe9174bfd918d764237b53015") + require.Equal(t, *status.Issuer.RevocationTreeRoot, "0000000000000000000000000000000000000000000000000000000000000000") + require.Equal(t, status.MTP.Existence, false) + +} diff --git a/resolvers/rhs.go b/resolvers/rhs.go new file mode 100644 index 0000000..77e9afc --- /dev/null +++ b/resolvers/rhs.go @@ -0,0 +1,205 @@ +package resolvers + +import ( + "bytes" + "context" + "math/big" + "net/url" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + core "github.com/iden3/go-iden3-core/v2" + "github.com/iden3/go-merkletree-sql/v2" + "github.com/iden3/go-schema-processor/v2/verifiable" + mp "github.com/iden3/merkletree-proof/http" + "github.com/pkg/errors" +) + +// RHSResolver is a struct that allows to interact with the RHS service to get revocation status. +type RHSResolver struct { + ethClients map[core.ChainID]*ethclient.Client + stateContractAddresses map[core.ChainID]common.Address +} + +// NewRHSResolver returns new RHS resolver +func NewRHSResolver(ethClients map[core.ChainID]*ethclient.Client, stateContractAddresses map[core.ChainID]common.Address) *RHSResolver { + return &RHSResolver{ + ethClients: ethClients, + stateContractAddresses: stateContractAddresses, + } +} + +// Resolve is a method to resolve a credential status from the RHS. +func (r RHSResolver) Resolve(ctx context.Context, + status verifiable.CredentialStatus) (out verifiable.RevocationStatus, err error) { + + if status.Type != verifiable.Iden3ReverseSparseMerkleTreeProof { + return out, errors.New("invalid status type") + } + + issuerDID := verifiable.GetIssuerDID(ctx) + if issuerDID == nil { + return out, errors.New("issuer DID is not set in context") + } + + issuerID, err := core.IDFromDID(*issuerDID) + if err != nil { + return out, err + } + + revNonce := new(big.Int).SetUint64(status.RevocationNonce) + + baseRHSURL, genesisState, err := rhsBaseURL(status.ID) + if err != nil { + return out, err + } + + ethClient, err := getEthClientForDID(issuerDID, r.ethClients) + if err != nil { + return out, err + } + + stateAddr, err := getStateContractForDID(issuerDID, r.stateContractAddresses) + if err != nil { + return out, err + } + state, err := identityStateForRHS(ctx, stateAddr, ethClient, &issuerID, genesisState) + if err != nil { + return out, err + } + + rhsCli, err := newRhsCli(baseRHSURL) + if err != nil { + return out, err + } + + out.Issuer, err = issuerFromRHS(ctx, *rhsCli, state) + if errors.Is(err, mp.ErrNodeNotFound) { + if genesisState != nil && state.Equals(genesisState) { + return out, errors.New("genesis state is not found in RHS") + } else { + return out, errors.New("current state is not found in RHS") + } + } else if err != nil { + return out, err + } + + revNonceHash, err := merkletree.NewHashFromBigInt(revNonce) + if err != nil { + return out, err + } + + revTreeRootHash, err := merkletree.NewHashFromHex(*out.Issuer.RevocationTreeRoot) + if err != nil { + return out, err + } + proof, err := rhsCli.GenerateProof(ctx, revTreeRootHash, + revNonceHash) + if err != nil { + return out, err + } + + out.MTP = *proof + + return out, nil +} + +func identityStateForRHS(ctx context.Context, stateAddr common.Address, ethClient *ethclient.Client, issuerID *core.ID, + genesisState *merkletree.Hash) (*merkletree.Hash, error) { + + state, err := lastStateFromContract(ctx, stateAddr, ethClient, issuerID) + if !errors.Is(err, errIdentityDoesNotExist) { + return state, err + } + + if genesisState == nil { + return nil, errors.New("current state is not found for the identity") + } + + stateIsGenesis, err := genesisStateMatch(genesisState, *issuerID) + if err != nil { + return nil, err + } + + if !stateIsGenesis { + return nil, errors.New("state is not genesis for the identity") + } + + return genesisState, nil +} + +// check if genesis state matches the state from the ID +func genesisStateMatch(state *merkletree.Hash, id core.ID) (bool, error) { + var tp [2]byte + copy(tp[:], id[:2]) + otherID, err := core.NewIDFromIdenState(tp, state.BigInt()) + if err != nil { + return false, err + } + return bytes.Equal(otherID[:], id[:]), nil +} + +func issuerFromRHS(ctx context.Context, rhsCli mp.ReverseHashCli, + state *merkletree.Hash) (verifiable.TreeState, error) { + + var issuer verifiable.TreeState + + stateNode, err := rhsCli.GetNode(ctx, state) + if err != nil { + return issuer, err + } + + if len(stateNode.Children) != 3 { + return issuer, errors.New( + "invalid state node, should have 3 children") + } + + stateHex := state.Hex() + issuer.State = &stateHex + claimsTreeRootHex := stateNode.Children[0].Hex() + issuer.ClaimsTreeRoot = &claimsTreeRootHex + revocationTreeRootHex := stateNode.Children[1].Hex() + issuer.RevocationTreeRoot = &revocationTreeRootHex + rootOfRootsHex := stateNode.Children[2].Hex() + issuer.RootOfRoots = &rootOfRootsHex + + return issuer, err +} + +func newRhsCli(rhsURL string) (*mp.ReverseHashCli, error) { + if rhsURL == "" { + return nil, errors.New("reverse hash service url is empty") + } + + return &mp.ReverseHashCli{ + URL: rhsURL, + HTTPTimeout: 10 * time.Second, + }, nil +} + +func rhsBaseURL(rhsURL string) (string, *merkletree.Hash, error) { + u, err := url.Parse(rhsURL) + if err != nil { + return "", nil, err + } + var state *merkletree.Hash + stateStr := u.Query().Get("state") + if stateStr != "" { + state, err = merkletree.NewHashFromHex(stateStr) + if err != nil { + return "", nil, err + } + } + + if strings.HasSuffix(u.Path, "/node") { + u.Path = strings.TrimSuffix(u.Path, "node") + } + if strings.HasSuffix(u.Path, "/node/") { + u.Path = strings.TrimSuffix(u.Path, "node/") + } + + u.RawQuery = "" + return u.String(), state, nil +} diff --git a/resolvers/rhs_test.go b/resolvers/rhs_test.go new file mode 100644 index 0000000..ed388c7 --- /dev/null +++ b/resolvers/rhs_test.go @@ -0,0 +1,60 @@ +package resolvers + +import ( + "context" + "encoding/json" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + core "github.com/iden3/go-iden3-core/v2" + "github.com/iden3/go-iden3-core/v2/w3c" + "github.com/iden3/go-schema-processor/v2/verifiable" + "github.com/jarcoal/httpmock" + "github.com/stretchr/testify/require" +) + +func TestRhsResolver(t *testing.T) { + credStatusJSON := `{ + "id": "https://rhs-staging.polygonid.me/node?state=f9dd6aa4e1abef52b6c94ab7eb92faf1a283b371d263e25ac835c9c04894741e", + "revocationNonce": 0, + "statusIssuer": { + "id": "https://ad40-91-210-251-7.ngrok-free.app/api/v1/identities/did%3Apolygonid%3Apolygon%3Amumbai%3A2qLGnFZiHrhdNh5KwdkGvbCN1sR2pUaBpBahAXC3zf/claims/revocation/status/0", + "revocationNonce": 0, + "type": "SparseMerkleTreeProof" + }, + "type": "Iden3ReverseSparseMerkleTreeProof" + }` + + var credStatus verifiable.CredentialStatus + err := json.Unmarshal([]byte(credStatusJSON), &credStatus) + require.NoError(t, err) + + issuerDID, err := w3c.ParseDID("did:polygonid:polygon:mumbai:2qLGnFZiHrhdNh5KwdkGvbCN1sR2pUaBpBahAXC3zf") + require.NoError(t, err) + stateAddr := common.HexToAddress("0x134B1BE34911E39A8397ec6289782989729807a4") + + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + httpmock.RegisterMatcherResponder("POST", "http://my-rpc/v2/1111", + httpmock.BodyContainsString(`{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"data":"0xb4bdea550010961e749448c0c935c85ae263d271b383a2f1fa92ebb74ac9b652efab1202","from":"0x0000000000000000000000000000000000000000","to":"0x134b1be34911e39a8397ec6289782989729807a4"},"latest"]}`), + httpmock.NewStringResponder(200, `{"jsonrpc":"2.0","id":1,"result":"0x0010961e749448c0c935c85ae263d271b383a2f1fa92ebb74ac9b652efab120209444d55300819a07594d731c0bf7d3713f5e9324e0435f926c3ef1d8e4a823400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000065846207000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000029cf4ff0000000000000000000000000000000000000000000000000000000000000000"}`)) + + httpmock.RegisterResponder("GET", "https://rhs-staging.polygonid.me/node/34824a8e1defc326f935044e32e9f513377dbfc031d79475a0190830554d4409", + httpmock.NewStringResponder(200, `{"node":{"hash":"34824a8e1defc326f935044e32e9f513377dbfc031d79475a0190830554d4409","children":["4436ea12d352ddb84d2ac7a27bbf7c9f1bfc7d3ff69f3e6cf4348f424317fd0b","0000000000000000000000000000000000000000000000000000000000000000","37eabc712cdaa64793561b16b8143f56f149ad1b0c35297a1b125c765d1c071e"]},"status":"OK"}`)) + + client, err := ethclient.Dial("http://my-rpc/v2/1111") + require.NoError(t, err) + var ethClients map[core.ChainID]*ethclient.Client = make(map[core.ChainID]*ethclient.Client) + ethClients[80001] = client + + var stateAddresses map[core.ChainID]common.Address = make(map[core.ChainID]common.Address) + stateAddresses[80001] = stateAddr + + rhsResolver := NewRHSResolver(ethClients, stateAddresses) + ctx := verifiable.WithIssuerDID(context.Background(), issuerDID) + _, err = rhsResolver.Resolve(ctx, credStatus) + require.NoError(t, err) + +} diff --git a/tests/integration/go.mod b/tests/integration/go.mod index a24ad06..237bfe5 100644 --- a/tests/integration/go.mod +++ b/tests/integration/go.mod @@ -7,7 +7,7 @@ require ( github.com/iden3/go-iden3-crypto v0.0.15 github.com/iden3/go-merkletree-sql/v2 v2.0.4 github.com/iden3/merkletree-proof v0.0.3 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.4 ) require ( @@ -15,21 +15,21 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/gorilla/websocket v1.4.2 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/holiman/uint256 v1.2.3 // indirect github.com/iden3/contracts-abi/rhs-storage/go/abi v0.0.0-20231006141557-7d13ef7e3c48 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect - golang.org/x/crypto v0.9.0 // indirect + golang.org/x/crypto v0.12.0 // indirect golang.org/x/exp v0.0.0-20230810033253-352e893a4cad // indirect - golang.org/x/sys v0.9.0 // indirect + golang.org/x/sys v0.15.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/tests/integration/go.sum b/tests/integration/go.sum index d077c20..eb76c90 100644 --- a/tests/integration/go.sum +++ b/tests/integration/go.sum @@ -17,15 +17,13 @@ github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/Yj github.com/consensys/gnark-crypto v0.10.0 h1:zRh22SR7o4K35SoNqouS9J/TKHTyU2QWaj5ldehyXtA= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= github.com/ethereum/go-ethereum v1.12.2 h1:eGHJ4ij7oyVqUQn48LBz3B7pvQ8sV0wGJiIE6gDq/6Y= github.com/ethereum/go-ethereum v1.12.2/go.mod h1:1cRAEV+rp/xX0zraSCBnu9Py3HQ+geRMj3HdR+k0wfI= @@ -45,8 +43,8 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= @@ -86,13 +84,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= @@ -102,24 +95,23 @@ github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZF github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/urfave/cli/v2 v2.24.1 h1:/QYYr7g0EhwXEML8jO+8OYt5trPnLHS0p3mrgExJ5NU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20230810033253-352e893a4cad h1:g0bG7Z4uG+OgH2QDODnjp6ggkk1bJDsINcuWmJN1iJU= golang.org/x/exp v0.0.0-20230810033253-352e893a4cad/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=