O Problema da Disponibilidade de Dados

iniciantes1/2/2024, 10:46:41 AM
Este artigo aborda a questão da disponibilidade de dados e como isso impacta a escalabilidade do Ethereum.

Como podem os pares em uma rede de blockchain ter certeza de que todos os dados de um novo bloco proposto estão disponíveis? E por que isso é importante?

Nesta postagem, exploramos os detalhes do problema de disponibilidade de dados e como isso pode impactar a escalabilidade no Ethereum.

Qual é o problema de disponibilidade de dados?

O problema de Disponibilidade de Dados (DA): Como podem os pares em uma rede blockchain ter certeza de que todos os dados de um bloco recém-proposto estão realmente disponíveis? Se os dados não estiverem disponíveis, o bloco pode conter transações maliciosas que estão sendo ocultadas pelo produtor do bloco. Mesmo que o bloco contenha transações não maliciosas, ocultá-las pode comprometer a segurança do sistema.

Para dar um exemplo, suponha que Alice seja uma operadora de um ZK-Rollup (ZKR). Ela envia uma Prova-ZK no Ethereum que é verificada. Se ela não enviar todos os dados transacionais no Ethereum, embora sua prova prove que todas as transições de estado realizadas no rollup são válidas, os usuários do rollup ainda podem estar no escuro sobre seus saldos de conta atuais. A prova enviada não lança luz sobre os estados atuais devido à natureza de Zero-Conhecimento dela.

Um exemplo análogo existe no Rollup otimista (OPR)configuração, onde Alice envia uma afirmação no Ethereum, mas nenhum dos participantes do OPR pode desafiá-la porque os dados transacionais não estão disponíveis e, portanto, não conseguem recalcular ou desafiar a afirmação.

Para enfrentar os cenários acima, ambos os designs OPR e ZKR exigem que os operadores enviem todos os detalhes transacionais sobreEthereumcomo 'calldata'. Embora isso os faça evitar o problema de DA a curto prazo, à medida que o número de transações cresce dentro dos rollups, a quantidade de dados que precisa ser enviada também cresceria, limitando a quantidade de escalabilidade que esses rollups podem oferecer.

Para piorar as coisas, a indisponibilidade de dados não é uma falha exclusivamente atribuível. Isso significa que os participantes não podem provar para outros pares que um determinado pedaço de dados está faltando. Isso ocorre porque Bob pode transmitir que o bloco enviado por Alice está com dados faltando, mas quando Charlie questiona Alice, ela pode fornecer os dados a ele.

Como isso afeta um blockchain hoje?

Para responder a essa pergunta, vamos primeiro revisitar a estrutura de bloco geral de uma blockchain semelhante ao Ethereum e os tipos de clientes que existem em qualquer rede blockchain.

Um bloco pode ser dividido em duas partes principais:

  • Cabeçalho do Bloco: Um cabeçalho de bloco pequeno contém o resumo e metadados relacionados às transações incluídas no bloco.
  • Corpo do Bloco: Isso contém todos os dados transacionais e compõe a maior parte do tamanho do bloco.

Em protocolos de blockchain convencionais, todos os nós são considerados como nós completos que sincronizam o bloco inteiro e verificam todas as transições de estado. Eles precisam gastar uma quantidade considerável de recursos para verificar a validade das transações e armazenar os blocos. Por outro lado, esses nós não podem ser forçados a aceitar transações inválidas.

Pode haver outra classe de nós que não têm (ou não querem gastar) recursos para verificar cada transação. Em vez disso, estão principalmente interessados em saber o estado atual da blockchain e se algumas transações, que são relevantes para eles, estão incluídas na cadeia ou não. Idealmente, esses clientes leves também devem estar protegidos de seguir uma cadeia que contenha transações inválidas. Isso é realmente possível usando chamados provas de fraude. Estas são mensagens sucintas que mostram que um determinado corpo de bloco inclui uma transação que é inválida. Qualquer nó completo pode produzir tal prova de fraude, e o cliente leve assim não tem que confiar que um determinado nó completo é honesto. Eles apenas têm que garantir que estão bem conectados a uma rede de fofocas que garanta que, se houver uma prova de fraude disponível para um cabeçalho de bloco, eles a receberão.

No entanto, há um problema com este sistema: e se um produtor de blocos não revelar todos os dados por trás de um bloco. Nesse caso, os nós completos obviamente rejeitarão o bloco, pois, em sua visão, nem sequer é um bloco se não vier com o corpo do bloco. Clientes leves, no entanto, podem ser apresentados com a cadeia de cabeçalhos e não têm como perceber que os dados estão faltando. Ao mesmo tempo, os nós completos não podem produzir provas de fraude, porque estariam faltando os dados necessários para criar provas de fraude.

Para combater isso, precisamos de um mecanismo para que os clientes leves verifiquem a disponibilidade dos dados. Isso garantiria que um produtor de blocos que esconde dados não possa escapar convencendo um cliente leve do contrário. Também forçaria o produtor de blocos a revelar partes dos dados, fazendo com que toda a rede tenha acesso ao bloco inteiro de maneira colaborativa.

Vamos aprofundar um pouco mais o problema com a ajuda de um exemplo. Suponha que a produtora de blocos Alice construa um bloco B com transações tx1, tx2, …, txn. Vamos supor que tx1 seja uma transação maliciosa. Se tx1 for transmitida, qualquer nó completo pode verificar que é maliciosa e enviar isso para um cliente leve como uma prova de fraude, que imediatamente saberia que o bloco é inaceitável. No entanto, se Alice quiser esconder tx1, ela revela o cabeçalho e todos os dados transacionais, exceto tx1. Os nós completos não podem verificar a correção de tx1.

Pode-se pensar que uma solução simples é se todos os clientes leves simplesmente amostrarem as transações aleatoriamente e, se encontrarem suas amostras disponíveis, podem ter a certeza de que o bloco está disponível. Deixe os nós leves consultarem qualquer transação, de forma uniforme e aleatória. A probabilidade de o cliente leve consultar tx1 é 1/n. Assim, com uma probabilidade avassaladora, Alice é capaz de enganar os clientes leves para aceitarem uma transação maliciosa. Em outras palavras, a maioria dos clientes leves será enganada. Devido à natureza não atribuível, os nós completos não podem provar de forma alguma que tx1 não está disponível. Infelizmente, aumentar o número de amostras não melhora muito isso.

Então, o que faremos sobre isso?

A solução para este problema reside em introduzir redundância em um bloco. Existe um rico conjunto de literatura sobre teoria da codificação em geral, e codificação por apagamento em particular, que pode nos ajudar com este problema.

Em poucas palavras, a codificação de apagamento nos permite estender qualquer n pedaços de dados em 2n pedaços de dados de forma que qualquer n de 2n seja suficiente para reconstruir a peça original de dados (os parâmetros são ajustáveis, mas aqui consideramos isso para simplicidade).

Se obrigarmos o produtor de bloco a codificar por apagamento as transações tx1, tx2, …, txn, então, para esconder uma única transação, seria necessário esconder n+1 fragmentos de dados, já que qualquer n é suficiente para construir todo o conjunto de transações. Neste caso, um número constante de consultas dá ao cliente leve uma confiança muito alta de que os dados subjacentes estão de fato disponíveis.

Woah, então é isso?

Embora este truque simples torne o trabalho de ocultação mais difícil, ainda é possível que o produtor de bloco simplesmente execute intencionalmente a codificação de apagamento de forma incorreta. No entanto, um nó completo pode verificar se esta codificação de apagamento foi feita corretamente e, caso contrário, pode provar isso a um cliente leve. Este é outro tipo de prova de fraude, assim como no caso de transações maliciosas acima. Curiosamente, precisa haver um único vizinho honesto do nó completo de um cliente leve para ter certeza de que, se o bloco for malicioso, ele receberá uma prova de fraude. Isso garante que o cliente leve tenha acesso a uma cadeia sem transação maliciosa com uma probabilidade muito alta.

Mas há uma pegadinha. Se feito de forma ingênua, o tamanho de algumas provas de fraude pode ser da ordem do tamanho do próprio bloco. A suposição de recursos que tínhamos sobre o cliente leve nos impede de usar esse design. Houve melhorias nesse sentido ao usar técnicas de codificação de apagamento multidimensional que reduzem o tamanho das provas de fraude ao custo do tamanho do comprometimento. Por uma questão de brevidade, não abordamos esses aspectos, mas este papeltem uma análise detalhada disso.

O problema com soluções baseadas em prova de fraude é que os clientes leves nunca têm certeza completa sobre qualquer bloco para o qual ainda não receberam uma prova de fraude. Além disso, eles continuam confiando que seus pares de nós completos são honestos. Nós honestos também precisam ser incentivados a continuar auditando blocos.

Nós focamos nossa atenção aqui em sistemas que garantem que, se a codificação do bloco for inválida, os nós completos podem detectá-la e fornecer prova aos clientes leves que os convençam do mau comportamento. No entanto, na próxima seção, vamos analisar as codificações de bloco que garantem que apenas codificações válidas possam ser confirmadas na cadeia. Isso elimina a necessidade de provas de fraude que comprovem erros de codificação. Essas soluções baseadas em provas de validade permitem que aplicativos usem o sistema sem ter que esperar por esse tipo de prova de fraude dos nós completos.

Então, como funcionam essas soluções?

Recentemente, os compromissos polinomiais têm despertado um interesse renovado no espaço blockchain. Esses compromissos polinomiais, especialmente o compromissos constantes de tamanho KZG/Kate para polinômios, pode ser usado para projetar um esquema DA limpo sem a necessidade de provas de fraude. Em resumo, os compromissos KZG nos permitem nos comprometer com um polinômio usando um único elemento de grupo de curva elíptica. Além disso, o esquema nos permite provar que em algum ponto i o polinômio φ avalia para φ(i) usando uma testemunha de tamanho constante. O esquema de compromisso é computacionalmente vinculativo e também é homomórfico, nos permitindo evitar fraudes de forma limpa.

Forçamos o produtor de bloco a pegar os dados transacionais originais e organizá-los em uma matriz 2D de tamanho n x m. Ele usa interpolação polinomial para estender cada coluna de tamanho n em colunas de tamanho 2n. Cada linha desta matriz estendida gera um compromisso polinomial e envia esses compromissos como parte do cabeçalho do bloco. Uma representação esquemática do bloco é dada abaixo.

Os clientes leves consultam qualquer célula desta matriz estendida para obter a testemunha que permite verificar imediatamente contra o cabeçalho do bloco. As provas de associação de tamanho constante tornam a amostragem extremamente eficiente. A natureza homomórfica do compromisso garante que a prova verifique apenas se o bloco for construído corretamente e a interpolação polinomial garante que um número constante de amostras bem-sucedidas significa que os dados estão disponíveis com uma probabilidade muito alta.

Uma representação esquemática do bloco

Os detalhes mais refinados do esquema juntamente com mais otimizações e estimativas de custo estão além do escopo deste artigo. No entanto, gostaríamos de salientar que, embora discutamos um esquema 2D aqui, garantias semelhantes podem ser fornecidas com um esquema 1D também, que tem um tamanho de cabeçalho menor ao custo de menos paralelismo e eficiência de amostragem do cliente leve. Vamos aprofundar isso em artigos de acompanhamento.

Quais são as outras alternativas e o que vem a seguir?

O código de apagamento de dimensão superior e os compromissos KZG não são as únicas maneiras de abordar o problema do DA. Há outras maneiras que deixamos de mencionar aqui como Árvores de Merkle codificadas, Árvore de Entrelaçamento Codificada, FRI, e abordagens baseadas em STARK, mas cada uma tem seus méritos e deméritos.

Na Avail, estamos trabalhando em uma solução de Disponibilidade de Dados usando compromissos KZG. Em postagens futuras, abordaremos os detalhes de implementação, como você pode usá-lo hoje e como pretendemos transformar o espaço de problemas de DA. Para mais informações sobre Avail, siga-nos emTwittere junte-se ao nossoservidor do Discord.

Aviso legal:

  1. Este artigo é reproduzido a partir de [GateEquipe Avail]. Todos os direitos autorais pertencem ao autor original [Equipe da Avail]. Se houver objeções a esta reimpressão, entre em contato com o [GateGate Aprender] equipe, e eles lidarão com isso prontamente.

  2. Responsabilidade de Isenção: As visões e opiniões expressas neste artigo são exclusivamente do autor e não constituem nenhum conselho de investimento.

  3. As traduções do artigo para outros idiomas são feitas pela equipe Gate Learn. A menos que seja mencionado, copiar, distribuir ou plagiar os artigos traduzidos é proibido.

O Problema da Disponibilidade de Dados

iniciantes1/2/2024, 10:46:41 AM
Este artigo aborda a questão da disponibilidade de dados e como isso impacta a escalabilidade do Ethereum.

Como podem os pares em uma rede de blockchain ter certeza de que todos os dados de um novo bloco proposto estão disponíveis? E por que isso é importante?

Nesta postagem, exploramos os detalhes do problema de disponibilidade de dados e como isso pode impactar a escalabilidade no Ethereum.

Qual é o problema de disponibilidade de dados?

O problema de Disponibilidade de Dados (DA): Como podem os pares em uma rede blockchain ter certeza de que todos os dados de um bloco recém-proposto estão realmente disponíveis? Se os dados não estiverem disponíveis, o bloco pode conter transações maliciosas que estão sendo ocultadas pelo produtor do bloco. Mesmo que o bloco contenha transações não maliciosas, ocultá-las pode comprometer a segurança do sistema.

Para dar um exemplo, suponha que Alice seja uma operadora de um ZK-Rollup (ZKR). Ela envia uma Prova-ZK no Ethereum que é verificada. Se ela não enviar todos os dados transacionais no Ethereum, embora sua prova prove que todas as transições de estado realizadas no rollup são válidas, os usuários do rollup ainda podem estar no escuro sobre seus saldos de conta atuais. A prova enviada não lança luz sobre os estados atuais devido à natureza de Zero-Conhecimento dela.

Um exemplo análogo existe no Rollup otimista (OPR)configuração, onde Alice envia uma afirmação no Ethereum, mas nenhum dos participantes do OPR pode desafiá-la porque os dados transacionais não estão disponíveis e, portanto, não conseguem recalcular ou desafiar a afirmação.

Para enfrentar os cenários acima, ambos os designs OPR e ZKR exigem que os operadores enviem todos os detalhes transacionais sobreEthereumcomo 'calldata'. Embora isso os faça evitar o problema de DA a curto prazo, à medida que o número de transações cresce dentro dos rollups, a quantidade de dados que precisa ser enviada também cresceria, limitando a quantidade de escalabilidade que esses rollups podem oferecer.

Para piorar as coisas, a indisponibilidade de dados não é uma falha exclusivamente atribuível. Isso significa que os participantes não podem provar para outros pares que um determinado pedaço de dados está faltando. Isso ocorre porque Bob pode transmitir que o bloco enviado por Alice está com dados faltando, mas quando Charlie questiona Alice, ela pode fornecer os dados a ele.

Como isso afeta um blockchain hoje?

Para responder a essa pergunta, vamos primeiro revisitar a estrutura de bloco geral de uma blockchain semelhante ao Ethereum e os tipos de clientes que existem em qualquer rede blockchain.

Um bloco pode ser dividido em duas partes principais:

  • Cabeçalho do Bloco: Um cabeçalho de bloco pequeno contém o resumo e metadados relacionados às transações incluídas no bloco.
  • Corpo do Bloco: Isso contém todos os dados transacionais e compõe a maior parte do tamanho do bloco.

Em protocolos de blockchain convencionais, todos os nós são considerados como nós completos que sincronizam o bloco inteiro e verificam todas as transições de estado. Eles precisam gastar uma quantidade considerável de recursos para verificar a validade das transações e armazenar os blocos. Por outro lado, esses nós não podem ser forçados a aceitar transações inválidas.

Pode haver outra classe de nós que não têm (ou não querem gastar) recursos para verificar cada transação. Em vez disso, estão principalmente interessados em saber o estado atual da blockchain e se algumas transações, que são relevantes para eles, estão incluídas na cadeia ou não. Idealmente, esses clientes leves também devem estar protegidos de seguir uma cadeia que contenha transações inválidas. Isso é realmente possível usando chamados provas de fraude. Estas são mensagens sucintas que mostram que um determinado corpo de bloco inclui uma transação que é inválida. Qualquer nó completo pode produzir tal prova de fraude, e o cliente leve assim não tem que confiar que um determinado nó completo é honesto. Eles apenas têm que garantir que estão bem conectados a uma rede de fofocas que garanta que, se houver uma prova de fraude disponível para um cabeçalho de bloco, eles a receberão.

No entanto, há um problema com este sistema: e se um produtor de blocos não revelar todos os dados por trás de um bloco. Nesse caso, os nós completos obviamente rejeitarão o bloco, pois, em sua visão, nem sequer é um bloco se não vier com o corpo do bloco. Clientes leves, no entanto, podem ser apresentados com a cadeia de cabeçalhos e não têm como perceber que os dados estão faltando. Ao mesmo tempo, os nós completos não podem produzir provas de fraude, porque estariam faltando os dados necessários para criar provas de fraude.

Para combater isso, precisamos de um mecanismo para que os clientes leves verifiquem a disponibilidade dos dados. Isso garantiria que um produtor de blocos que esconde dados não possa escapar convencendo um cliente leve do contrário. Também forçaria o produtor de blocos a revelar partes dos dados, fazendo com que toda a rede tenha acesso ao bloco inteiro de maneira colaborativa.

Vamos aprofundar um pouco mais o problema com a ajuda de um exemplo. Suponha que a produtora de blocos Alice construa um bloco B com transações tx1, tx2, …, txn. Vamos supor que tx1 seja uma transação maliciosa. Se tx1 for transmitida, qualquer nó completo pode verificar que é maliciosa e enviar isso para um cliente leve como uma prova de fraude, que imediatamente saberia que o bloco é inaceitável. No entanto, se Alice quiser esconder tx1, ela revela o cabeçalho e todos os dados transacionais, exceto tx1. Os nós completos não podem verificar a correção de tx1.

Pode-se pensar que uma solução simples é se todos os clientes leves simplesmente amostrarem as transações aleatoriamente e, se encontrarem suas amostras disponíveis, podem ter a certeza de que o bloco está disponível. Deixe os nós leves consultarem qualquer transação, de forma uniforme e aleatória. A probabilidade de o cliente leve consultar tx1 é 1/n. Assim, com uma probabilidade avassaladora, Alice é capaz de enganar os clientes leves para aceitarem uma transação maliciosa. Em outras palavras, a maioria dos clientes leves será enganada. Devido à natureza não atribuível, os nós completos não podem provar de forma alguma que tx1 não está disponível. Infelizmente, aumentar o número de amostras não melhora muito isso.

Então, o que faremos sobre isso?

A solução para este problema reside em introduzir redundância em um bloco. Existe um rico conjunto de literatura sobre teoria da codificação em geral, e codificação por apagamento em particular, que pode nos ajudar com este problema.

Em poucas palavras, a codificação de apagamento nos permite estender qualquer n pedaços de dados em 2n pedaços de dados de forma que qualquer n de 2n seja suficiente para reconstruir a peça original de dados (os parâmetros são ajustáveis, mas aqui consideramos isso para simplicidade).

Se obrigarmos o produtor de bloco a codificar por apagamento as transações tx1, tx2, …, txn, então, para esconder uma única transação, seria necessário esconder n+1 fragmentos de dados, já que qualquer n é suficiente para construir todo o conjunto de transações. Neste caso, um número constante de consultas dá ao cliente leve uma confiança muito alta de que os dados subjacentes estão de fato disponíveis.

Woah, então é isso?

Embora este truque simples torne o trabalho de ocultação mais difícil, ainda é possível que o produtor de bloco simplesmente execute intencionalmente a codificação de apagamento de forma incorreta. No entanto, um nó completo pode verificar se esta codificação de apagamento foi feita corretamente e, caso contrário, pode provar isso a um cliente leve. Este é outro tipo de prova de fraude, assim como no caso de transações maliciosas acima. Curiosamente, precisa haver um único vizinho honesto do nó completo de um cliente leve para ter certeza de que, se o bloco for malicioso, ele receberá uma prova de fraude. Isso garante que o cliente leve tenha acesso a uma cadeia sem transação maliciosa com uma probabilidade muito alta.

Mas há uma pegadinha. Se feito de forma ingênua, o tamanho de algumas provas de fraude pode ser da ordem do tamanho do próprio bloco. A suposição de recursos que tínhamos sobre o cliente leve nos impede de usar esse design. Houve melhorias nesse sentido ao usar técnicas de codificação de apagamento multidimensional que reduzem o tamanho das provas de fraude ao custo do tamanho do comprometimento. Por uma questão de brevidade, não abordamos esses aspectos, mas este papeltem uma análise detalhada disso.

O problema com soluções baseadas em prova de fraude é que os clientes leves nunca têm certeza completa sobre qualquer bloco para o qual ainda não receberam uma prova de fraude. Além disso, eles continuam confiando que seus pares de nós completos são honestos. Nós honestos também precisam ser incentivados a continuar auditando blocos.

Nós focamos nossa atenção aqui em sistemas que garantem que, se a codificação do bloco for inválida, os nós completos podem detectá-la e fornecer prova aos clientes leves que os convençam do mau comportamento. No entanto, na próxima seção, vamos analisar as codificações de bloco que garantem que apenas codificações válidas possam ser confirmadas na cadeia. Isso elimina a necessidade de provas de fraude que comprovem erros de codificação. Essas soluções baseadas em provas de validade permitem que aplicativos usem o sistema sem ter que esperar por esse tipo de prova de fraude dos nós completos.

Então, como funcionam essas soluções?

Recentemente, os compromissos polinomiais têm despertado um interesse renovado no espaço blockchain. Esses compromissos polinomiais, especialmente o compromissos constantes de tamanho KZG/Kate para polinômios, pode ser usado para projetar um esquema DA limpo sem a necessidade de provas de fraude. Em resumo, os compromissos KZG nos permitem nos comprometer com um polinômio usando um único elemento de grupo de curva elíptica. Além disso, o esquema nos permite provar que em algum ponto i o polinômio φ avalia para φ(i) usando uma testemunha de tamanho constante. O esquema de compromisso é computacionalmente vinculativo e também é homomórfico, nos permitindo evitar fraudes de forma limpa.

Forçamos o produtor de bloco a pegar os dados transacionais originais e organizá-los em uma matriz 2D de tamanho n x m. Ele usa interpolação polinomial para estender cada coluna de tamanho n em colunas de tamanho 2n. Cada linha desta matriz estendida gera um compromisso polinomial e envia esses compromissos como parte do cabeçalho do bloco. Uma representação esquemática do bloco é dada abaixo.

Os clientes leves consultam qualquer célula desta matriz estendida para obter a testemunha que permite verificar imediatamente contra o cabeçalho do bloco. As provas de associação de tamanho constante tornam a amostragem extremamente eficiente. A natureza homomórfica do compromisso garante que a prova verifique apenas se o bloco for construído corretamente e a interpolação polinomial garante que um número constante de amostras bem-sucedidas significa que os dados estão disponíveis com uma probabilidade muito alta.

Uma representação esquemática do bloco

Os detalhes mais refinados do esquema juntamente com mais otimizações e estimativas de custo estão além do escopo deste artigo. No entanto, gostaríamos de salientar que, embora discutamos um esquema 2D aqui, garantias semelhantes podem ser fornecidas com um esquema 1D também, que tem um tamanho de cabeçalho menor ao custo de menos paralelismo e eficiência de amostragem do cliente leve. Vamos aprofundar isso em artigos de acompanhamento.

Quais são as outras alternativas e o que vem a seguir?

O código de apagamento de dimensão superior e os compromissos KZG não são as únicas maneiras de abordar o problema do DA. Há outras maneiras que deixamos de mencionar aqui como Árvores de Merkle codificadas, Árvore de Entrelaçamento Codificada, FRI, e abordagens baseadas em STARK, mas cada uma tem seus méritos e deméritos.

Na Avail, estamos trabalhando em uma solução de Disponibilidade de Dados usando compromissos KZG. Em postagens futuras, abordaremos os detalhes de implementação, como você pode usá-lo hoje e como pretendemos transformar o espaço de problemas de DA. Para mais informações sobre Avail, siga-nos emTwittere junte-se ao nossoservidor do Discord.

Aviso legal:

  1. Este artigo é reproduzido a partir de [GateEquipe Avail]. Todos os direitos autorais pertencem ao autor original [Equipe da Avail]. Se houver objeções a esta reimpressão, entre em contato com o [GateGate Aprender] equipe, e eles lidarão com isso prontamente.

  2. Responsabilidade de Isenção: As visões e opiniões expressas neste artigo são exclusivamente do autor e não constituem nenhum conselho de investimento.

  3. As traduções do artigo para outros idiomas são feitas pela equipe Gate Learn. A menos que seja mencionado, copiar, distribuir ou plagiar os artigos traduzidos é proibido.

Начните торговать сейчас
Зарегистрируйтесь сейчас и получите ваучер на
$100
!