Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EIP 7702 #7237

Merged
merged 100 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from 89 commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
619f4a1
EIP 7702 first draft
daniellehrner Jun 18, 2024
6420df0
added CHANGELOG.md entry
daniellehrner Jun 18, 2024
73bd969
bug fixes, added first tests
daniellehrner Jun 20, 2024
52ee0db
container verify GitHub workflow (#7239)
cdivitotawela Jun 19, 2024
a3beda6
Investigate chain halts when syncing (#7162)
pinges Jun 20, 2024
76c9b6a
Check for EOFCreate subcontainer rules (#7232)
shemnon Jun 20, 2024
030721d
Remove deprecation message for `--Xp2p-peer-lower-bound` (#7247)
Gabriel-Trintinalia Jun 21, 2024
f4e6b76
less invasive code injection approach
daniellehrner Jun 21, 2024
2db71af
added missing java doc & fixed test
daniellehrner Jun 21, 2024
f133219
added (currently non-working) acceptance test, some bug fixes in the …
daniellehrner Jun 22, 2024
64f5f73
fix spotless
daniellehrner Jun 23, 2024
f2c6b48
updated acceptance test, still not working, newPayload request seems …
daniellehrner Jun 24, 2024
59827f9
use correct world state to inject temporary code, inject code in exis…
daniellehrner Jun 24, 2024
123ce12
renamed test service to prague, because the engine versions used are …
daniellehrner Jun 24, 2024
1dad4d1
fixed acceptance test, some bug fixes if authorized account does not …
daniellehrner Jun 25, 2024
bef375e
Add build version option to prefix git hash with custom version prope…
matthew1001 Jun 21, 2024
4c4452f
Handle invalid snap getTrieNode requests with empty paths gracefully …
jframe Jun 24, 2024
d8480c3
fix typos in CHANGELOG (#7226)
TiesD Jun 24, 2024
e3e3693
feat: Add network option for LUKSO Mainnet (#7223)
Wolmin Jun 24, 2024
96bbfd5
Update Docker base image to Ubuntu 24.04 (#7251)
fab-10 Jun 24, 2024
117df5c
Reconfigure how Protocol Specs are created WRT EVM condiguration (#7245)
shemnon Jun 24, 2024
63ff928
Fix the wrong 'Identifier' and 'Synchronizer' usage (#7252)
leniram159 Jun 24, 2024
f62c0cc
Fix flaky SECP256R1 test (#7249)
daniellehrner Jun 25, 2024
3b73512
update to work with the new max retries value (#7253)
jflo Jun 25, 2024
841e2e8
Temporary CancunEOF fork for EOF testing. (#7227)
shemnon Jun 25, 2024
89714ad
fixed bug introduced through merge of main, made acceptance test easi…
daniellehrner Jun 25, 2024
71105a7
added missing java docs
daniellehrner Jun 25, 2024
5eac657
removed unnecessary tag
daniellehrner Jun 25, 2024
6a56ff8
make encodeSingleSetCode public again
daniellehrner Jun 25, 2024
7fcc42f
Snapserver responses to return at least one response (#7190)
jframe Jun 26, 2024
5def93b
copy setCodeTransactionPayloads as well
daniellehrner Jun 26, 2024
1f8fe15
fixed bug during tests with forrest db
daniellehrner Jun 26, 2024
bd66e81
Snapserver GetTrieNodes request to handle short hash for storage (#7264)
jframe Jun 26, 2024
9e1cb81
javadoc: Adding javadoc for ethstats module (#7269)
usmansaleem Jun 27, 2024
d1416f1
Fix javadoc for ethereum:core top level package (#7270)
usmansaleem Jun 27, 2024
d3d1bde
Disable Flaky tests - permissioning (#7256)
macfarla Jun 27, 2024
744a253
Add bootnodes to the maintained peer list (#7257)
matthew1001 Jun 27, 2024
3b60f93
Fix javadoc for ethereum api module, graphql package (#7272)
usmansaleem Jun 28, 2024
beaee51
T8n support for isStateTest and empty accounts (#7275)
shemnon Jun 28, 2024
bea55c9
Promote storage x-trie-log subcommand to trie-log (#7278)
siladu Jun 28, 2024
0ff94af
Evm tool readme update (#7274)
jflo Jun 28, 2024
0af6053
javadoc - Add missing javadoc for evmtool module (#7277)
usmansaleem Jun 28, 2024
da83bd9
Rename ValidatorPublicKey to ValidatorPubKey (#7280)
shemnon Jun 28, 2024
508269d
Add info-level diagnostic logs to aid with resolving stalled BFT chai…
matthew1001 Jul 1, 2024
143f866
Update EIP-2935 contract (#7281)
shemnon Jul 1, 2024
e032b60
add evmtool compability, fixing bugs related to sender recovery of 77…
daniellehrner Jul 4, 2024
5a72c85
Deeper tracing of self-destructed accounts (#7284)
shemnon Jul 1, 2024
eb9325f
next release version after 24.7.0 (#7285)
garyschulte Jul 1, 2024
54f38d4
Add experimental `--Xsnapsync-bft-enabled` which enables snap sync fo…
matthew1001 Jul 2, 2024
3769485
Turn off CicleCI for Besu (#7291)
cdivitotawela Jul 3, 2024
55a2f1f
Check for snap server (#6609)
pinges Jul 3, 2024
aa62fb2
Implement System Calls (#7263)
Gabriel-Trintinalia Jul 4, 2024
d6f3470
wrap WorldUpdater inside a WorldUpdaterService to inject the authoriz…
daniellehrner Jul 9, 2024
4424feb
Update limit trie logs validation message for sync-mode=FULL (#7279)
siladu Jul 4, 2024
1cd2cf9
Execute requests before block persist (#7295)
Gabriel-Trintinalia Jul 5, 2024
18c4239
fixed MainnetTransactionProcessor retrieval of correctn `to` account …
daniellehrner Jul 9, 2024
dd0a79e
only first authorization is accepted, all the following ones are ignored
daniellehrner Jul 10, 2024
70da1a6
don't cache account with empty code
daniellehrner Jul 10, 2024
52c1afd
revert wrapping of world updater, as its `updater()` method creates a…
daniellehrner Jul 10, 2024
59629ea
Fixed outdated tech redirect link. (#7297)
snazzysam933 Jul 10, 2024
d2c8ff8
Increment private nonce even if transaction failed. (#6593)
gtebrean Jul 10, 2024
4ae8caf
feat: Enhance --profile to load external profiles (#7292)
usmansaleem Jul 10, 2024
0e199e5
Fix status badge for documentation (#7304)
cdivitotawela Jul 10, 2024
48477bc
[MINOR] Fixed some typos (#7299)
macfarla Jul 10, 2024
429a5b9
refactored to share one AuthorizedAccountService between the differen…
daniellehrner Jul 11, 2024
1852b3f
spotless
daniellehrner Jul 11, 2024
373ecab
load code for authorization at the beginning of the transaction
daniellehrner Jul 11, 2024
9b2b07e
ignore authorization if chain id doesn't match
daniellehrner Jul 11, 2024
5d035af
cache authority address, evmtool: do not fail if sender address is wrong
daniellehrner Jul 15, 2024
1207ff1
Add evmtool block-test subcommand (#7293)
shemnon Jul 12, 2024
50dd40f
Make the retrying snap tasks switching (#7307)
pinges Jul 12, 2024
641d13f
6612: Remove deprecated sync modes and related helper methods (#7309)
Matilda-Clerke Jul 12, 2024
745aa7d
EOF Reference Test Fixes (#7306)
shemnon Jul 12, 2024
d22c9c6
test template refactor, bump besu-native to 0.9.2 (#7315)
garyschulte Jul 12, 2024
cf878ed
Feature/use gnark-crypto for eip-2537 (#7316)
garyschulte Jul 14, 2024
b981f30
6612 update changelog with removed syncmodes (#7320)
Matilda-Clerke Jul 15, 2024
5035d9a
Update datacopy (#7319)
shemnon Jul 15, 2024
35ebb7a
disable flaky test (#7308)
macfarla Jul 15, 2024
0eb329b
Update unit test (#7317)
shemnon Jul 15, 2024
62ced3d
persist accounts that have storage updates, but no nonce, balance nor…
daniellehrner Jul 15, 2024
6e5b1d7
Revert "persist accounts that have storage updates, but no nonce, bal…
daniellehrner Jul 16, 2024
22ac25c
removed PKI backed QBFT (#7310)
macfarla Jul 16, 2024
cac6cf3
EIP-7251 add consolidation request type (#7266)
macfarla Jul 16, 2024
4ff426c
fix: `eth_call` deserialization to correctly ignore unknown fields in…
usmansaleem Jul 16, 2024
283ab8b
Stop transaction selection on TX_EVALUATION_TOO_LONG (#7330)
fab-10 Jul 16, 2024
4eeb5f0
message frame buider will create AuthorizedCodeService by itsef if it…
daniellehrner Jul 16, 2024
c63d4fa
Merge branch 'main' into feat/issue-7205/eip-7702v1
daniellehrner Jul 16, 2024
eea2281
get correct nonce for authorization
daniellehrner Jul 16, 2024
22e6ff6
nonce only returns a vaid nonce, new method nonceList returns all the…
daniellehrner Jul 16, 2024
4f32fee
plugs leaky abstraction
jflo Jul 16, 2024
0f7df16
Merge pull request #1 from jflo/optionalNonce
daniellehrner Jul 17, 2024
51c8e87
some renaming, acceptance tests checks for exact balance of tx sponso…
daniellehrner Jul 17, 2024
1c77b78
Merge branch 'main' into feat/issue-7205/eip-7702v1
jflo Jul 17, 2024
6642f4f
inject the 7702 code into DiffBasedWorldStateUpdateAccumulator.create…
daniellehrner Jul 17, 2024
761ac41
Merge branch 'main' into feat/issue-7205/eip-7702v1
daniellehrner Jul 17, 2024
e534fc1
Merge branch 'feat/issue-7205/eip-7702v1' of github.com:daniellehrner…
daniellehrner Jul 17, 2024
e6ec88d
spotless
jflo Jul 17, 2024
8c71d2b
spotless fix, removed todos
daniellehrner Jul 17, 2024
f6bf975
Merge branch 'feat/issue-7205/eip-7702v1' of github.com:daniellehrner…
daniellehrner Jul 17, 2024
299f65f
make AuthorityProcessor & chain id for it optional
daniellehrner Jul 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- `privacy-nonce-always-increments` option enables private transactions to always increment the nonce, even if the transaction is invalid [#6593](https://github.com/hyperledger/besu/pull/6593)
- Add `block-test` subcommand to the evmtool which runs blockchain reference tests [#7310](https://github.com/hyperledger/besu/pull/7310)
- Implement gnark-crypto for eip-2537 [#7316](https://github.com/hyperledger/besu/pull/7316)
- Added EIP-7702 [#7237](https://github.com/hyperledger/besu/pull/7237)

### Bug fixes
- Fix `eth_call` deserialization to correctly ignore unknown fields in the transaction object. [#7323](https://github.com/hyperledger/besu/pull/7323)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.ethereum;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.eth.EthTransactions;

import java.io.IOException;
import java.util.Optional;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.web3j.protocol.core.methods.response.EthBlock;

public class PragueAcceptanceTestService {
protected static final MediaType MEDIA_TYPE_JSON =
MediaType.parse("application/json; charset=utf-8");

private final OkHttpClient httpClient;
private final ObjectMapper mapper;
private final BesuNode besuNode;
private final EthTransactions ethTransactions;

private long blockTimeStamp = 0;

PragueAcceptanceTestService(final BesuNode besuNode, final EthTransactions ethTransactions) {
daniellehrner marked this conversation as resolved.
Show resolved Hide resolved
this.besuNode = besuNode;
this.ethTransactions = ethTransactions;
httpClient = new OkHttpClient();
mapper = new ObjectMapper();
}

public void buildNewBlock() throws IOException {
final EthBlock.Block block = besuNode.execute(ethTransactions.block());

blockTimeStamp += 1;
final Call buildBlockRequest =
createEngineCall(createForkChoiceRequest(block.getHash(), blockTimeStamp));

final String payloadId;
try (final Response buildBlockResponse = buildBlockRequest.execute()) {
payloadId =
mapper
.readTree(buildBlockResponse.body().string())
.get("result")
.get("payloadId")
.asText();

assertThat(payloadId).isNotEmpty();
}

waitFor(500);

final Call getPayloadRequest = createEngineCall(createGetPayloadRequest(payloadId));

final ObjectNode executionPayload;
final String newBlockHash;
final String parentBeaconBlockRoot;
try (final Response getPayloadResponse = getPayloadRequest.execute()) {
assertThat(getPayloadResponse.code()).isEqualTo(200);

executionPayload =
(ObjectNode)
mapper
.readTree(getPayloadResponse.body().string())
.get("result")
.get("executionPayload");

newBlockHash = executionPayload.get("blockHash").asText();
parentBeaconBlockRoot = executionPayload.remove("parentBeaconBlockRoot").asText();

assertThat(newBlockHash).isNotEmpty();
}

final Call newPayloadRequest =
createEngineCall(
createNewPayloadRequest(executionPayload.toString(), parentBeaconBlockRoot));
try (final Response newPayloadResponse = newPayloadRequest.execute()) {
assertThat(newPayloadResponse.code()).isEqualTo(200);
}

final Call moveChainAheadRequest = createEngineCall(createForkChoiceRequest(newBlockHash));

try (final Response moveChainAheadResponse = moveChainAheadRequest.execute()) {
assertThat(moveChainAheadResponse.code()).isEqualTo(200);
}
}

private Call createEngineCall(final String request) {
return httpClient.newCall(
new Request.Builder()
.url(besuNode.engineRpcUrl().get())
.post(RequestBody.create(request, MEDIA_TYPE_JSON))
.build());
}

private String createForkChoiceRequest(final String blockHash) {
return createForkChoiceRequest(blockHash, null);
}

private String createForkChoiceRequest(final String parentBlockHash, final Long timeStamp) {
final Optional<Long> maybeTimeStamp = Optional.ofNullable(timeStamp);

String forkChoiceRequest =
"{"
+ " \"jsonrpc\": \"2.0\","
+ " \"method\": \"engine_forkchoiceUpdatedV3\","
+ " \"params\": ["
+ " {"
+ " \"headBlockHash\": \""
+ parentBlockHash
+ "\","
+ " \"safeBlockHash\": \""
+ parentBlockHash
+ "\","
+ " \"finalizedBlockHash\": \""
+ parentBlockHash
+ "\""
+ " }";

if (maybeTimeStamp.isPresent()) {
forkChoiceRequest +=
" ,{"
+ " \"timestamp\": \""
+ Long.toHexString(maybeTimeStamp.get())
+ "\","
+ " \"prevRandao\": \"0x0000000000000000000000000000000000000000000000000000000000000000\","
+ " \"suggestedFeeRecipient\": \"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b\","
+ " \"withdrawals\": [],"
+ " \"parentBeaconBlockRoot\": \"0x0000000000000000000000000000000000000000000000000000000000000000\""
+ " }";
}

forkChoiceRequest += " ]," + " \"id\": 67" + "}";

return forkChoiceRequest;
}

private String createGetPayloadRequest(final String payloadId) {
return "{"
+ " \"jsonrpc\": \"2.0\","
+ " \"method\": \"engine_getPayloadV4\","
+ " \"params\": [\""
+ payloadId
+ "\"],"
+ " \"id\": 67"
+ "}";
}

private String createNewPayloadRequest(
final String executionPayload, final String parentBeaconBlockRoot) {
return "{"
+ " \"jsonrpc\": \"2.0\","
+ " \"method\": \"engine_newPayloadV4\","
+ " \"params\": ["
+ executionPayload
+ ",[],"
+ "\""
+ parentBeaconBlockRoot
+ "\""
+ "],"
+ " \"id\": 67"
+ "}";
}

private static void waitFor(final long durationMilliSeconds) {
try {
Thread.sleep(durationMilliSeconds);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.tests.acceptance.ethereum;

import static org.assertj.core.api.Assertions.assertThat;

import org.hyperledger.besu.crypto.SECP256K1;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.SetCodeAuthorization;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;

import java.io.IOException;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;

import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.web3j.protocol.core.methods.response.TransactionReceipt;

public class SetCodeTransactionAcceptanceTest extends AcceptanceTestBase {
private static final String GENESIS_FILE = "/dev/dev_prague.json";
private static final SECP256K1 secp256k1 = new SECP256K1();

public static final Address SEND_ALL_ETH_CONTRACT_ADDRESS =
Address.fromHexStringStrict("0000000000000000000000000000000000009999");

private final Account authorizer =
accounts.createAccount(
Address.fromHexStringStrict("8da48afC965480220a3dB9244771bd3afcB5d895"));
public static final Bytes AUTHORIZER_PRIVATE_KEY =
Bytes.fromHexString("11f2e7b6a734ab03fa682450e0d4681d18a944f8b83c99bf7b9b4de6c0f35ea1");

private final Account transactionSponsor =
accounts.createAccount(
Address.fromHexStringStrict("a05b21E5186Ce93d2a226722b85D6e550Ac7D6E3"));
public static final Bytes TRANSACTION_SPONSOR_PRIVATE_KEY =
Bytes.fromHexString("3a4ff6d22d7502ef2452368165422861c01a0f72f851793b372b87888dc3c453");

private BesuNode besuNode;
private PragueAcceptanceTestService testService;

@BeforeEach
void setUp() throws IOException {
besuNode = besu.createExecutionEngineGenesisNode("besuNode", GENESIS_FILE);
cluster.start(besuNode);

testService = new PragueAcceptanceTestService(besuNode, ethTransactions);
}

/**
* At the beginning of the test both the authorizer and the transaction sponsor have a balance of
* 90000 ETH. The authorizer creates an authorization for a contract that send all its ETH to any
* given address. The transaction sponsor created a 7702 transaction with it and sends all the ETH
* from the authorizer to itself. The authorizer balance should be 0 and the transaction sponsor
* balance should be greater than 170000 ETH.
*/
@Test
public void shouldTransferAllEthOfAuthorizerToSponsor() throws IOException {

// 7702 transaction
final org.hyperledger.besu.datatypes.SetCodeAuthorization authorization =
SetCodeAuthorization.builder()
.chainId(BigInteger.valueOf(20211))
.address(SEND_ALL_ETH_CONTRACT_ADDRESS)
.signAndBuild(
secp256k1.createKeyPair(
secp256k1.createPrivateKey(AUTHORIZER_PRIVATE_KEY.toUnsignedBigInteger())));

final Transaction tx =
Transaction.builder()
.type(TransactionType.SET_CODE)
.chainId(BigInteger.valueOf(20211))
.nonce(0)
.maxPriorityFeePerGas(Wei.of(1000000000))
.maxFeePerGas(Wei.fromHexString("0x02540BE400"))
.gasLimit(1000000)
.to(Address.fromHexStringStrict(authorizer.getAddress()))
.value(Wei.ZERO)
.payload(Bytes32.leftPad(Bytes.fromHexString(transactionSponsor.getAddress())))
.accessList(List.of())
.setCodeTransactionPayloads(List.of(authorization))
.signAndBuild(
secp256k1.createKeyPair(
secp256k1.createPrivateKey(
TRANSACTION_SPONSOR_PRIVATE_KEY.toUnsignedBigInteger())));

final String txHash =
besuNode.execute(ethTransactions.sendRawTransaction(tx.encoded().toHexString()));
testService.buildNewBlock();

cluster.verify(authorizer.balanceEquals(0));

final BigInteger transactionSponsorBalance =
besuNode.execute(ethTransactions.getBalance(transactionSponsor));

// The transaction sponsor balance should be greater than 170000 ETH. We don't do an exact
// balance check to avoid
// having to calculate the exact amount of gas used.
assertThat(transactionSponsorBalance).isGreaterThan(new BigInteger("170000000000000000000000"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not also assert that rugee now has zero balance?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be resolved, it is currently on line 111


Optional<TransactionReceipt> maybeTransactionReceipt =
besuNode.execute(ethTransactions.getTransactionReceipt(txHash));
assertThat(maybeTransactionReceipt).isPresent();
}
}
111 changes: 111 additions & 0 deletions acceptance-tests/tests/src/test/resources/dev/dev_prague.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ public void maxPrioritizedTxsPerTypeConfigFile() throws IOException {
@Test
public void maxPrioritizedTxsPerTypeWrongTxType() {
internalTestFailure(
"Invalid value for option '--tx-pool-max-prioritized-by-type' (MAP<TYPE,INTEGER>): expected one of [FRONTIER, ACCESS_LIST, EIP1559, BLOB] (case-insensitive) but was 'WRONG_TYPE'",
"Invalid value for option '--tx-pool-max-prioritized-by-type' (MAP<TYPE,INTEGER>): expected one of [FRONTIER, ACCESS_LIST, EIP1559, BLOB, SET_CODE] (case-insensitive) but was 'WRONG_TYPE'",
"--tx-pool-max-prioritized-by-type",
"WRONG_TYPE=1");
}
Expand Down
Loading
Loading