Skip to content

Commit ac360d7

Browse files
committed
Add signet test vectors
1 parent 0f2b93e commit ac360d7

23 files changed

Lines changed: 840 additions & 0 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package uniresolver.driver.did.btc1.signet.k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import foundation.identity.did.DID;
5+
import foundation.identity.did.DIDDocument;
6+
import org.erdtman.jcs.JsonCanonicalizer;
7+
import org.junit.jupiter.api.Test;
8+
import uniresolver.driver.did.btc1.crud.read.Read;
9+
import uniresolver.driver.did.btc1.syntax.DidBtc1IdentifierDecoding;
10+
import uniresolver.driver.did.btc1.syntax.records.IdentifierComponents;
11+
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
15+
import static org.junit.jupiter.api.Assertions.assertEquals;
16+
17+
public class ResolveInitialDocumentTest {
18+
19+
private static final ObjectMapper objectMapper = new ObjectMapper();
20+
21+
@Test
22+
public void testResolveInitialDIDDocument() throws Exception {
23+
24+
Map<String, Object> didDocumentMetadata = new HashMap<>();
25+
26+
Read read = new Read(TestUtil.testBitcoinConnections(), TestUtil.testIpfsConnection());
27+
28+
DID identifier = DID.fromString(TestUtil.readResourceString("did.txt"));
29+
Map<String, Object> resolutionOptions = TestUtil.readResourceJson("resolutionOptions.json");
30+
31+
IdentifierComponents identifierComponents = DidBtc1IdentifierDecoding.didBtc1IdentifierDecoding(identifier);
32+
33+
DIDDocument initialDIDDocument = read.getResolveInitialDocument().resolveInitialDIDDocument(identifier, identifierComponents, resolutionOptions, didDocumentMetadata);
34+
String initialDIDDocumentCanonicalized = new JsonCanonicalizer(initialDIDDocument.toJson()).getEncodedString();
35+
Map<String, Object> initialDIDDocumentMap = (Map<String, Object>) objectMapper.readValue(initialDIDDocumentCanonicalized, Map.class);
36+
37+
DIDDocument expectedInitialDIDDocument = DIDDocument.fromMap(TestUtil.readResourceJson("initialDidDoc.json"));
38+
String expectedInitialDIDDocumentCanonicalized = new JsonCanonicalizer(expectedInitialDIDDocument.toJson()).getEncodedString();
39+
Map<String, Object> expectedInitialDIDDocumentMap = (Map<String, Object>) objectMapper.readValue(expectedInitialDIDDocumentCanonicalized, Map.class);
40+
41+
assertEquals(expectedInitialDIDDocumentCanonicalized, initialDIDDocumentCanonicalized);
42+
assertEquals(expectedInitialDIDDocumentMap, initialDIDDocumentMap);
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package uniresolver.driver.did.btc1.signet.k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import foundation.identity.did.DID;
5+
import foundation.identity.did.DIDDocument;
6+
import org.erdtman.jcs.JsonCanonicalizer;
7+
import org.junit.jupiter.api.Test;
8+
import uniresolver.driver.did.btc1.crud.read.Read;
9+
import uniresolver.driver.did.btc1.syntax.DidBtc1IdentifierDecoding;
10+
import uniresolver.driver.did.btc1.syntax.records.IdentifierComponents;
11+
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
15+
import static org.junit.jupiter.api.Assertions.assertEquals;
16+
17+
public class ResolveTargetDocumentTest {
18+
19+
private static final ObjectMapper objectMapper = new ObjectMapper();
20+
21+
@Test
22+
public void testResolveTargetDIDDocument() throws Exception {
23+
24+
Map<String, Object> didDocumentMetadata = new HashMap<>();
25+
26+
Read read = new Read(TestUtil.testBitcoinConnections(), TestUtil.testIpfsConnection());
27+
28+
DID identifier = DID.fromString(TestUtil.readResourceString("did.txt"));
29+
Map<String, Object> resolutionOptions = TestUtil.readResourceJson("resolutionOptions.json");
30+
31+
IdentifierComponents identifierComponents = DidBtc1IdentifierDecoding.didBtc1IdentifierDecoding(identifier);
32+
33+
DIDDocument initialDIDDocument = read.getResolveInitialDocument().resolveInitialDIDDocument(identifier, identifierComponents, resolutionOptions, didDocumentMetadata);
34+
35+
DIDDocument targetDIDDocument = read.getResolveTargetDocument().resolveTargetDocument(initialDIDDocument, resolutionOptions, identifierComponents.network(), didDocumentMetadata);
36+
String targetDIDDocumentCanonicalized = new JsonCanonicalizer(targetDIDDocument.toJson()).getEncodedString();
37+
Map<String, Object> targetDIDDocumentMap = (Map<String, Object>) objectMapper.readValue(targetDIDDocumentCanonicalized, Map.class);
38+
39+
DIDDocument expectedTargetDIDDocument = DIDDocument.fromMap(TestUtil.readResourceJson("targetDocument.json"));
40+
String expectedTargetDIDDocumentCanonicalized = new JsonCanonicalizer(expectedTargetDIDDocument.toJson()).getEncodedString();
41+
Map<String, Object> expectedTargetDIDDocumentMap = (Map<String, Object>) objectMapper.readValue(expectedTargetDIDDocumentCanonicalized, Map.class);
42+
43+
assertEquals(expectedTargetDIDDocumentCanonicalized, targetDIDDocumentCanonicalized);
44+
assertEquals(expectedTargetDIDDocumentMap, targetDIDDocumentMap);
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package uniresolver.driver.did.btc1.signet.k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import uniresolver.driver.did.btc1.Network;
5+
import uniresolver.driver.did.btc1.connections.bitcoin.BitcoinConnector;
6+
import uniresolver.driver.did.btc1.connections.bitcoin.EsploraElectrsRESTBitcoinConnection;
7+
import uniresolver.driver.did.btc1.connections.ipfs.IPFSConnection;
8+
9+
import java.io.IOException;
10+
import java.net.URI;
11+
import java.net.URISyntaxException;
12+
import java.nio.charset.StandardCharsets;
13+
import java.nio.file.Files;
14+
import java.nio.file.Paths;
15+
import java.util.Map;
16+
17+
public class TestUtil {
18+
19+
private static final ObjectMapper objectMapper = new ObjectMapper();
20+
21+
static String readResourceString(String resourceName) {
22+
try {
23+
return Files.readString(Paths.get(TestUtil.class.getResource(resourceName).toURI()), StandardCharsets.UTF_8);
24+
} catch (URISyntaxException | IOException ex) {
25+
throw new RuntimeException(ex.getMessage(), ex);
26+
}
27+
}
28+
29+
static Map<String, Object> readResourceJson(String resourceName) throws IOException {
30+
return (Map<String, Object>) objectMapper.readValue(readResourceString(resourceName), Map.class);
31+
}
32+
33+
static BitcoinConnector testBitcoinConnections() {
34+
return BitcoinConnector.create(
35+
Map.of(
36+
Network.signet, EsploraElectrsRESTBitcoinConnection.create(URI.create("https://mempool.space/signet/api/"))),
37+
Map.of(
38+
Network.signet, "f61eee3b63a380a477a063af32b2bbc97c9ff9f01f2c4225e973988108000000")
39+
);
40+
}
41+
42+
static IPFSConnection testIpfsConnection() {
43+
return IPFSConnection.create("/dns4/localhost/tcp/5001");
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package uniresolver.driver.did.btc1.signet.x1qyj23twdpn927d5ky2f5ulgmr9uudq2pd08gxy05fdjzxvfclzn2zazps8w;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import foundation.identity.did.DID;
5+
import foundation.identity.did.DIDDocument;
6+
import org.erdtman.jcs.JsonCanonicalizer;
7+
import org.junit.jupiter.api.Test;
8+
import uniresolver.driver.did.btc1.crud.read.Read;
9+
import uniresolver.driver.did.btc1.syntax.DidBtc1IdentifierDecoding;
10+
import uniresolver.driver.did.btc1.syntax.records.IdentifierComponents;
11+
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
15+
import static org.junit.jupiter.api.Assertions.assertEquals;
16+
17+
public class ResolveInitialDocumentTest {
18+
19+
private static final ObjectMapper objectMapper = new ObjectMapper();
20+
21+
@Test
22+
public void testResolveInitialDIDDocument() throws Exception {
23+
24+
Map<String, Object> didDocumentMetadata = new HashMap<>();
25+
26+
Read read = new Read(TestUtil.testBitcoinConnections(), TestUtil.testIpfsConnection());
27+
28+
DID identifier = DID.fromString(TestUtil.readResourceString("did.txt"));
29+
Map<String, Object> resolutionOptions = TestUtil.readResourceJson("resolutionOptions.json");
30+
31+
IdentifierComponents identifierComponents = DidBtc1IdentifierDecoding.didBtc1IdentifierDecoding(identifier);
32+
33+
DIDDocument initialDIDDocument = read.getResolveInitialDocument().resolveInitialDIDDocument(identifier, identifierComponents, resolutionOptions, didDocumentMetadata);
34+
String initialDIDDocumentCanonicalized = new JsonCanonicalizer(initialDIDDocument.toJson()).getEncodedString();
35+
Map<String, Object> initialDIDDocumentMap = (Map<String, Object>) objectMapper.readValue(initialDIDDocumentCanonicalized, Map.class);
36+
37+
DIDDocument expectedInitialDIDDocument = DIDDocument.fromMap(TestUtil.readResourceJson("initialDidDoc.json"));
38+
String expectedInitialDIDDocumentCanonicalized = new JsonCanonicalizer(expectedInitialDIDDocument.toJson()).getEncodedString();
39+
Map<String, Object> expectedInitialDIDDocumentMap = (Map<String, Object>) objectMapper.readValue(expectedInitialDIDDocumentCanonicalized, Map.class);
40+
41+
assertEquals(expectedInitialDIDDocumentCanonicalized, initialDIDDocumentCanonicalized);
42+
assertEquals(expectedInitialDIDDocumentMap, initialDIDDocumentMap);
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package uniresolver.driver.did.btc1.signet.x1qyj23twdpn927d5ky2f5ulgmr9uudq2pd08gxy05fdjzxvfclzn2zazps8w;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import foundation.identity.did.DID;
5+
import foundation.identity.did.DIDDocument;
6+
import org.erdtman.jcs.JsonCanonicalizer;
7+
import org.junit.jupiter.api.Test;
8+
import uniresolver.driver.did.btc1.crud.read.Read;
9+
import uniresolver.driver.did.btc1.syntax.DidBtc1IdentifierDecoding;
10+
import uniresolver.driver.did.btc1.syntax.records.IdentifierComponents;
11+
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
15+
import static org.junit.jupiter.api.Assertions.assertEquals;
16+
17+
public class ResolveTargetDocumentTest {
18+
19+
private static final ObjectMapper objectMapper = new ObjectMapper();
20+
21+
@Test
22+
public void testResolveTargetDIDDocument() throws Exception {
23+
24+
Map<String, Object> didDocumentMetadata = new HashMap<>();
25+
26+
Read read = new Read(TestUtil.testBitcoinConnections(), TestUtil.testIpfsConnection());
27+
28+
DID identifier = DID.fromString(TestUtil.readResourceString("did.txt"));
29+
Map<String, Object> resolutionOptions = TestUtil.readResourceJson("resolutionOptions.json");
30+
31+
IdentifierComponents identifierComponents = DidBtc1IdentifierDecoding.didBtc1IdentifierDecoding(identifier);
32+
33+
DIDDocument initialDIDDocument = read.getResolveInitialDocument().resolveInitialDIDDocument(identifier, identifierComponents, resolutionOptions, didDocumentMetadata);
34+
35+
DIDDocument targetDIDDocument = read.getResolveTargetDocument().resolveTargetDocument(initialDIDDocument, resolutionOptions, identifierComponents.network(), didDocumentMetadata);
36+
String targetDIDDocumentCanonicalized = new JsonCanonicalizer(targetDIDDocument.toJson()).getEncodedString();
37+
Map<String, Object> targetDIDDocumentMap = (Map<String, Object>) objectMapper.readValue(targetDIDDocumentCanonicalized, Map.class);
38+
39+
DIDDocument expectedTargetDIDDocument = DIDDocument.fromMap(TestUtil.readResourceJson("targetDocument.json"));
40+
String expectedTargetDIDDocumentCanonicalized = new JsonCanonicalizer(expectedTargetDIDDocument.toJson()).getEncodedString();
41+
Map<String, Object> expectedTargetDIDDocumentMap = (Map<String, Object>) objectMapper.readValue(expectedTargetDIDDocumentCanonicalized, Map.class);
42+
43+
assertEquals(expectedTargetDIDDocumentCanonicalized, targetDIDDocumentCanonicalized);
44+
assertEquals(expectedTargetDIDDocumentMap, targetDIDDocumentMap);
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package uniresolver.driver.did.btc1.signet.x1qyj23twdpn927d5ky2f5ulgmr9uudq2pd08gxy05fdjzxvfclzn2zazps8w;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import uniresolver.driver.did.btc1.Network;
5+
import uniresolver.driver.did.btc1.connections.bitcoin.BitcoinConnector;
6+
import uniresolver.driver.did.btc1.connections.bitcoin.EsploraElectrsRESTBitcoinConnection;
7+
import uniresolver.driver.did.btc1.connections.ipfs.IPFSConnection;
8+
9+
import java.io.IOException;
10+
import java.net.URI;
11+
import java.net.URISyntaxException;
12+
import java.nio.charset.StandardCharsets;
13+
import java.nio.file.Files;
14+
import java.nio.file.Paths;
15+
import java.util.Map;
16+
17+
public class TestUtil {
18+
19+
private static final ObjectMapper objectMapper = new ObjectMapper();
20+
21+
static String readResourceString(String resourceName) {
22+
try {
23+
return Files.readString(Paths.get(TestUtil.class.getResource(resourceName).toURI()), StandardCharsets.UTF_8);
24+
} catch (URISyntaxException | IOException ex) {
25+
throw new RuntimeException(ex.getMessage(), ex);
26+
}
27+
}
28+
29+
static Map<String, Object> readResourceJson(String resourceName) throws IOException {
30+
return (Map<String, Object>) objectMapper.readValue(readResourceString(resourceName), Map.class);
31+
}
32+
33+
static BitcoinConnector testBitcoinConnections() {
34+
return BitcoinConnector.create(
35+
Map.of(
36+
Network.signet, EsploraElectrsRESTBitcoinConnection.create(URI.create("https://mempool.space/signet/api/"))),
37+
Map.of(
38+
Network.signet, "f61eee3b63a380a477a063af32b2bbc97c9ff9f01f2c4225e973988108000000")
39+
);
40+
}
41+
42+
static IPFSConnection testIpfsConnection() {
43+
return IPFSConnection.create("/dns4/localhost/tcp/5001");
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"@context":["https://w3id.org/security/v2","https://w3id.org/zcap/v1","https://w3id.org/json-ld-patch/v1"],"patch":[{"op":"add","path":"/verificationMethod/1","value":{"controller":"did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4","id":"did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#key-1","publicKeyMultibase":"zQ3shnJ534uiR8xHXZuumXDDeAYwJhkTbiyYcaFSbwWxCnpXT","type":"Multikey"}}],"proof":{"@context":["https://w3id.org/security/v2","https://w3id.org/zcap/v1","https://w3id.org/json-ld-patch/v1"],"capability":"urn:zcap:root:did%3Abtc1%3Ak1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4","capabilityAction":"Write","cryptosuite":"bip340-jcs-2025","proofPurpose":"capabilityInvocation","proofValue":"z3X7gg4Kzxb1UDWdZspKy1gaSa5gB22cybBBn2BgCeobpnUrqjaSMdnwqD1jjRQ6NoQ8KMQecSKToiR9RyTfNuyWw","type":"DataIntegrityProof","verificationMethod":"did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#initialKey"},"sourceHash":"2JQZ5BGNrWD63ybsRLMNX3QCwF26Hz4oeSbyTJKbpXqF","targetHash":"Hin1Bqqf1doGPrdKGyMLJt6hEfoCMhS6yrT3meRxxKfv","targetVersionId":2}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"@context": [
3+
"https://www.w3.org/TR/did-1.1",
4+
"https://did-btc1/TBD/context"
5+
],
6+
"id": "did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4",
7+
"controller": [
8+
"did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4"
9+
],
10+
"verificationMethod": [
11+
{
12+
"id": "did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#initialKey",
13+
"type": "Multikey",
14+
"controller": "did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4",
15+
"publicKeyMultibase": "zQ3shuKkwqUmqPcMXxP9bpDCyurRu5UfEy9nybzSXnPwvFLzY"
16+
},
17+
{
18+
"id": "did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#key-1",
19+
"type": "Multikey",
20+
"controller": "did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4",
21+
"publicKeyMultibase": "zQ3shnJ534uiR8xHXZuumXDDeAYwJhkTbiyYcaFSbwWxCnpXT"
22+
}
23+
],
24+
"authentication": [
25+
"did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#initialKey"
26+
],
27+
"assertionMethod": [
28+
"did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#initialKey"
29+
],
30+
"capabilityInvocation": [
31+
"did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#initialKey"
32+
],
33+
"capabilityDelegation": [
34+
"did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#initialKey"
35+
],
36+
"service": [
37+
{
38+
"id": "did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#initialP2PKH",
39+
"type": "SingletonBeacon",
40+
"serviceEndpoint": "bitcoin:mtA1SshFsJtD2Di1KBSTmyuD23eBqUekQ3"
41+
},
42+
{
43+
"id": "did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#initialP2WPKH",
44+
"type": "SingletonBeacon",
45+
"serviceEndpoint": "bitcoin:tb1q323c0l0fapjeg4ux9ayumnpqh8xzqgk3wg82dy"
46+
},
47+
{
48+
"id": "did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#initialP2TR",
49+
"type": "SingletonBeacon",
50+
"serviceEndpoint": "bitcoin:tb1pecc8w64wdvn6x2np8yr8qvsz2pclydkd9t5jde2gf0hy0musfxxsn23q20"
51+
}
52+
]
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3beb201e710bc93a3c633351b13d6b5b8a2583ec02631832ec16061e9337845f
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[
2+
{
3+
"@context": [
4+
"https://w3id.org/security/v2",
5+
"https://w3id.org/zcap/v1",
6+
"https://w3id.org/json-ld-patch/v1"
7+
],
8+
"patch": [
9+
{
10+
"op": "add",
11+
"path": "/verificationMethod/1",
12+
"value": {
13+
"id": "did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#key-1",
14+
"type": "Multikey",
15+
"controller": "did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4",
16+
"publicKeyMultibase": "zQ3shnJ534uiR8xHXZuumXDDeAYwJhkTbiyYcaFSbwWxCnpXT"
17+
}
18+
}
19+
],
20+
"sourceHash": "2JQZ5BGNrWD63ybsRLMNX3QCwF26Hz4oeSbyTJKbpXqF",
21+
"targetHash": "Hin1Bqqf1doGPrdKGyMLJt6hEfoCMhS6yrT3meRxxKfv",
22+
"targetVersionId": 2,
23+
"proof": {
24+
"type": "DataIntegrityProof",
25+
"cryptosuite": "bip340-jcs-2025",
26+
"verificationMethod": "did:btc1:k1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4#initialKey",
27+
"proofPurpose": "capabilityInvocation",
28+
"capability": "urn:zcap:root:did%3Abtc1%3Ak1qypa5tq86fzrl0ez32nh8e0ks4tzzkxnnmn8tdvxk04ahzt70u09dagl0mgs4",
29+
"capabilityAction": "Write",
30+
"@context": [
31+
"https://w3id.org/security/v2",
32+
"https://w3id.org/zcap/v1",
33+
"https://w3id.org/json-ld-patch/v1"
34+
],
35+
"proofValue": "z3X7gg4Kzxb1UDWdZspKy1gaSa5gB22cybBBn2BgCeobpnUrqjaSMdnwqD1jjRQ6NoQ8KMQecSKToiR9RyTfNuyWw"
36+
}
37+
}
38+
]

0 commit comments

Comments
 (0)