Boas tardes a todos! Estiven facendo desenvolvemento de backend durante bastante tempo e, nos últimos anos, fun escribindo cada vez máis proxectos de cadea de bloques diferentes (Solidity on EVM). Mergullo na cadea de bloques non foi doado para min, e o meu cerebro de backend rompeuse varias veces, polo que decidín compartir a miña opinión sobre o cambio ao desenvolvemento da cadea de bloques.
Descargo de responsabilidade: todo o que se describe a continuación é puramente a miña opinión. Podería estar equivocado, e estou equivocado regularmente :))
Blockchain é unha tecnoloxía moi interesante que pode facer avanzar o noso mundo. Pero, polo de agora, moita xente úsao para comprar, vender, estafar, aumentar a fraude. Non vou e non penso considerar a cripto como un activo.
Si, moitos vídeos e publicacións diferentes din: "Aquí, tal e cal cripto pode crecer agora, e tal e cal cripto caeu. Rapaces, investimos, compremos...” Non vos direi nada diso.
Só direi unha cousa: deberías evitar a criptografía para gañar cartos. Se es un programador, é mellor intentar gañar como programador e non involucrarse en investimentos. E se realmente queres investir, non entres en cripto.
Paga a pena comezar cunha nota importante. Blockchain non é criptomoeda. A cadea de bloques é a tecnoloxía na que se constrúe a criptomoeda e chamar a cadea de bloques á criptomoeda é como chamar Javascript a toda a industria do desenvolvemento. Si, JavaScript parece ser un caso especial de desenvolvemento, pero cando falamos de desenvolvemento, non nos referimos a JavaScript. Aínda que algunhas persoas só se refiren a JavaScript...
O primeiro que se me ocorre é o diñeiro. Os desenvolvedores de Blockchain páganse bastante ben. Eu persoalmente abrín este tipo de prazas. Respondín persoalmente a este tipo de vacantes, onde podes conseguir máis que o mesmo programador de backend pola mesma cantidade de tempo dedicado ao día no traballo. Os desenvolvedores de blockchain valen o seu peso en ouro nas startups baseadas en blockchain. Especialmente un bo programador de blockchain!
Converterse nun bo desenvolvedor backend é imposible sen deixar caer ou arranxar un produto. Quizais só sexa un perdedor e só aprendo a través de experiencias negativas, pero esa é a teoría que teño:
Non podes intercambiar experiencia co fracaso, pero a experiencia do fracaso permíteche entender que tes experiencia. É difícil converterse nun bo programador de blockchain sen perder cartos:
A imaxe de arriba mostra o primeiro avión dos irmáns Wright no mundo, voando bastante mal. Pero voou, e no seu día, a actitude da persoa media ante os avións era algo así:
E agora, a industria aérea é unha parte marabillosa das nosas vidas, conectando persoas de todo o planeta en poucas horas. A loxística está agora nun nivel co que os irmáns Wright nunca soñaron! O mundo enteiro vive diferente por mor dos avións.
Agora diría o mesmo sobre blockchain: é caro e inconveniente e non está claro por que. Ata que estiven inmerso no desenvolvemento da cadea de bloques, parecíame algo inútil para enganar aos investimentos (=hamsters). Pero se o miras desde o outro lado, é posible almacenar calquera feito de forma descentralizada sen posibilidade de manipulación. O "sen posibilidade de manipulación" é un detalle importante.
Pero, por desgraza, as asociacións coa palabra "blockchain" son bastante aburridas e monótonas:
Se botamos un feixe de papel diñeiro na cociña, a cociña arderá ben, e o diñeiro incluso dará calor durante algún tempo. Pero iso é absurdo.
E así sucede coa cadea de bloques. Usalo só para cartos e criptomonedas é malo, pero a outra cousa aínda non está enraizando...
Un dos principais motores do desenvolvemento de calquera produto é o diñeiro. Se hai algo para gañar cartos bos e poderosos, entón este "algo" desenvolverase activamente. E é por iso que, ata agora, os proxectos financeiros baseados na cadea de bloques baséanse realmente en gañar cartos para que alguén gañe cartos e en perder cartos por outra persoa.
Cal é a diferenza entre un servizo centralizado e descentralizado? Comecemos cun sistema centralizado. Alí estamos eu e outra persoa, e decidimos utilizar un servizo entre nós, como un banco ou outro provedor, para facilitar unha transferencia.
Imaxinemos que temos un banco, que é un servizo centralizado. Mando a este banco: "Por favor, transfira 100 dólares a esta persoa". O banco rexistra que eu teño 100 dólares menos, e outra persoa ten 100 dólares máis.
Pero cal é o problema do servizo centralizado? Hai un propietario detrás deste servizo centralizado, non? Normalmente, algunhas grandes empresas, holdings, non importa; no noso caso, que sexa unha persoa. Esta persoa pode dicir: "Imos facer isto. Deixa que Alex envíe 100 dólares, pero ninguén recibirá estes 100 dólares. Necesitoos máis”.
Os servizos centralizados teñen os seus propietarios. O problema é que o propietario pode tomar decisións negativas, quitando diñeiro. E non pode tratarse só de "tomar o diñeiro para ti". Por exemplo, pode escribir: "Temos 100.500 dólares no banco" e esperar que todos os depositantes non vaian tras ese diñeiro... como pasou con SVB e outros bancos que morreron.
Bitcoin foi inventado para descentralizar a xestión do diñeiro.
Un servizo descentralizado constrúese nunha rede de nodos onde cada nodo almacena e transmite información. En pocas palabras, os nodos acordaron que información se considera correcta e que non e como a almacenamos.
Podemos facer unha analoxía cunha habitación: unha persoa grita a información que quere que os demais conserven. Despois, todos na sala traballan coa información que gardaron despois do berro.
Por exemplo, unha cadea de bloques pode almacenar e transmitir mensaxes ou información sobre transferencias de diñeiro. Os participantes na rede verifican a información antes de gravala.
Na analoxía da sala, grito: "Estou transferindo 100 dólares a Sam". Todo o mundo rexistra que eu teño 100 dólares menos, e Sam ten 100 dólares máis. Se de súpeto, antes da transferencia, teño menos de 100 dólares, ninguén rexistrará a transacción.
En blockchain, podes crear contratos intelixentes na linguaxe Solidity (no caso de blockchain en EVM). Un contrato intelixente é un programa que se executa na rede blockchain. Pode conter mecanismos de validación, tratamento de erros e outras funcións.
De novo, no caso da sala na que gritamos comandos, un contrato intelixente é que me entregue a cada participante da sala o código do programa con antelación: como reaccionar aos meus comandos, que comprobar e que gardar. E entón grito: "Executar o programa con estes parámetros". Despois, todos seguen as instrucións. Un exemplo de contrato sinxelo e intelixente é o almacenamento de información coas funcións de engadir e recibir datos. O código do contrato compílase en código de bytes e pásase aos participantes da cadea de bloques para a súa execución.
Como xestionamos as solicitudes particulares que se enviarán a este contrato intelixente? Se trazamos unha analoxía co backend, é un servizo que pode procesar solicitudes POST e GET. POST almacena información. GET aquí devolve a información que almacenamos. Normalmente é así como se estrutura calquera backend.
Durante o meu desenvolvemento no backend, acostumeime moito ao arranxo de que a API, a base de datos e todo o relacionado co almacenamento e procesamento de datos ocorren do meu lado. E eu xa, coma detrás dunha parede, proporciono unha interface para que o usuario traballe con estes datos segundo un escenario previamente preparado.
Por exemplo, o usuario 1 vén e garda contido (publicación, por exemplo) a través do método POST. Despois, o usuario 2 chega e recupera este contido mediante o método GET. Os usuarios non saben onde e como se atopa: o backend é unha caixa negra para eles.
E aquí chegamos a unha parte moi importante da cadea de bloques. Volvamos aos nosos exemplos de nodos ou persoas paradas nunha sala. Digamos que, usando unha analoxía co backend, temos o seguinte cada vez: lanzo un método "ADD" á cadea de bloques e, a continuación, todos chaman localmente ao método e despois poden tomar información da súa copia da cadea de bloques.
Entón, temos unha morea de copias diferentes na rede das que os nodos toman información. O problema coa cadea de bloques é que debemos pagar diñeiro real por cada operación de escritura. Isto págase coa moeda da rede, que se pode comprar con diñeiro real (ou extraer, pero non é do que falamos hoxe).
Se comparamos blockchain e backend, a imaxe é a seguinte:
Por exemplo, Telegram ten unha base de datos centralizada. Sempre podemos acceder a el gratuitamente e descargar as nosas mensaxes, fotos, vídeos, etc. Pero se os servidores de Telegram caen de súpeto, non podemos acceder a el.
Temos que pagar pola máquina virtual EVM para executar algúns comandos de contrato intelixente, incluíndo escribir información na cadea de bloques. Realiza algúns cálculos, suma algo, multiplica, multiplica, multiplica e, finalmente, aparece un novo artefacto no almacenamento da cadea de bloques, que se actualiza en todos os nós que participan na cadea de bloques.
Calquera participante da rede pode executar un nodo completo con centos de gigabytes de datos blockchain e traballar con el localmente. Tamén podes usar unha versión lixeira do nodo, que non almacenará toda a cadea de bloques, pero podes acceder aos nodos completos da rede e recuperar a información necesaria a través dela.
A idea é que cada entrada na cadea de bloques sexa un bloque que contén unha morea de transaccións nas que se producen cambios no estado da cadea de bloques. Cada bloque sucesivo depende do anterior nunha cadea baseada en algoritmos de hash.
En xeral, é básico, pero vale a pena telo en conta: tes que pagar por cada espirro se os datos cambian. Por certo, un despliegue de contrato tamén é un récord na cadea de bloques e non é barato!
No mundo do backend, estou afeito ao seguinte ciclo de vida de desenvolvemento de funcións:
É dicir, estamos afeitos a traballar deste xeito, e pasa de balde. Aínda que condicionalmente gratuíto, porque pagamos os servidores. Que pasa coa cadea de bloques?
No caso da cadea de bloques, necesitamos escribir o novo código da nosa "aplicación" (contrato intelixente) na cadea de bloques. Como escribín arriba, temos que pagar por cada disco. Antes de realizar unha transacción co noso contrato intelixente, necesitamos realizar unha transacción coa colocación do contrato intelixente.
Despois, o servidor cliente/servizo poñerase en contacto con calquera dos nodos para recibir ou gardar información no contrato.
Hai que notificar un gran número de nodos: "Rapaces, aquí está o bytecode do contrato cuxos algoritmos hai que facer nas miñas transaccións". É imprescindible asegurarse de que o mesmo código apareza en todos os nodos que aprenden a cadea de bloques, e executarase do mesmo xeito, independentemente de quen o chame e independentemente de como se chame. A mecánica será a mesma e inalterable. Ademais, non hai forma de que o contrato intelixente poida modificarse dalgún xeito para funcionar de forma diferente en ningún dos nodos.
A continuación móstrase un exemplo dunha transacción na que depositei un contrato na rede ETH hai moito tempo.
Foi un contrato de proba que nunca se utilizou en realidade. Paguei 200 dólares en ETH polo seu despregamento. É dicir, aínda non fixemos nada con este contrato, nin unha soa solicitude, pero xa se gastaron 200 dólares. Aínda estou triste cando recordo este despregamento equivocado do contrato equivocado...
Falemos do almacenamento de datos. Todos estamos afeitos a ter PostgreSQL , MySQL , MongoDB , Redis e outros servizos no backend que nos permiten traballar con datos de forma conveniente. No caso da cadea de bloques, non hai nada parecido nin preto.
En blockchain, o almacenamento implícase como variables nunha clase noutros idiomas. É dicir, só valores clave ou matrices. Non hai táboas relacionais con ligazóns convenientes, etc. Só: escribe a unha variable e sé feliz.
Polo momento, non coñezo ningunha outra forma de organizar o almacenamento en blockchain. Pois quizais a situación xa cambiou; quizais cando estás lendo isto, hai tal forma: escribe nos comentarios.
Por exemplo, se queremos almacenar non só nunha matriz? E queremos almacenar información por clave: hai cartografía para iso.
O sinal de dólar está debuxado por un motivo: a comisión de rede tomarase por cada conxunto.
Neste bloque, comentarei cousas que me sorprenderon ou me enfadaron. Hai moitos máis dos que hai neste documento, pero compartirei as primeiras cousas que me golpearon na miña práctica.
É importante ter en conta que a maioría das "dores" son comprensibles por algún motivo lóxico. Pero iso non cancela as dores do meu cerebro de fondo.
Por exemplo, estou afeito ao feito de que podemos pasar facilmente por todos os elementos de calquera cousa. Non importa se é unha matriz ou un obxecto ou un mapa. En Solidity, para tal fin, teremos que almacenar por separado unha matriz de todas as claves e despois, se é necesario, percorrer todas elas e recuperar elementos do mapa para cada clave. Ben, tamén gastamos gas en escribir adicionais a esta matriz de claves e na súa inicialización.
Tampouco podemos obter todas as cousas útiles para clasificar as teclas.
A situación co rexistro tamén é desagradable. Estou afeito a depurar a través do depurador no ambiente de desenvolvemento, pero aquí deberías esquecerte incluso do rexistro normal.
En Typescript, estou afeito a escribir console.log(a)
e obter inmediatamente a saída na consola. En Solidity, hai console.log
, que só funciona cando se executa no entorno de desenvolvemento local de cascos . E, o xenial é que despois de dividir o que necesito, teño que eliminar todos estes rexistros antes de que se implemente o contrato porque, se non, o contrato pesa máis e custa máis implementar, e non funcionará en absoluto no produto. .
Ao final resulta que cando executamos o proxecto xa en batalla, queremos ver o que está mal, non podemos ver o que saíu mal. Pero podemos ver o que foi ben. Hai un sistema de eventos dentro dos contratos intelixentes. Aquí tes un exemplo: digamos que queremos ter un evento no que se engade un novo elemento baixo este índice con este valor.
Chamamos este evento dentro do método set
, e só podemos ver os rexistros cando se executa con éxito. Se algo saíu mal, tiveches varias chamadas a contratos ou tivemos un fallo de transacción, tampouco se gardan os rexistros porque a información da cadea de bloques se retrotrae.
Supoña que usa unha cadea de varios contratos intelixentes. Tes no primeiro contrato chamado uns eventos, despois chámase o segundo contrato, que chama outros eventos, e despois cae todo o que se chamou dentro do segundo contrato. Todo será eliminado exactamente dunha vez por todas.
Temos que ter moito coidado cando queremos rexistrar o que está a suceder dentro da cadea de bloques e ter en conta que o rexistro normal, ao que estamos afeitos, simplemente non está dispoñible aquí.
Outra cousa desagradable é que non podemos obter información das nosas transaccións nunha función de escritura. Se facemos unha transacción que escribe algo na cadea de bloques (é dicir, unha transacción de pago), a return
non dará nada ao noso servizo que se integre co contrato intelixente. Esta devolución só funciona dentro do contrato intelixente ou view
funcións (gratuítas).
Por exemplo, gustaríanos que cando engadimos un novo valor á nosa cadea de bloques, quizais queiramos descubrir o tamaño de almacenamento despois de gardar (captura de pantalla anterior). É dicir, só podemos descubrir a través dos eventos o que se engadiu exactamente. E para iso, necesitamos tirar eventos que se desencadearon dentro desa transacción.
Houbo unha sorpresa para min aquí: é imposible traballar con cordas normalmente. A cadea de bloques non se creou para cadeas. Pasemos aos exemplos.
O seguinte código funcionará sen ningún problema.
E este código xa non funcionará:
Hai tempo que estiven afeito a traballar normalmente con cadeas, cambiar caracteres en cadeas, cortar cadeas, concatenalas, todo isto non está dispoñible fóra da caixa. Tampouco hai posibilidade de mostrar a lonxitude da cadea. É dicir, este código non compilará:
Se realmente precisa a lonxitude dunha cadea, pode convertela en bytes e despois contar o número de bytes. Pero o problema é que algúns caracteres especiais non se converten en bytes 1v1. E algúns simplemente non se converten e a transacción pode fallar.
Poderías acabar escribindo un contrato intelixente que manexa cadeas e probas en cadeas normais. Entón, chegará unha cadea que non se procesará e todo fallará ou a lonxitude da cadea contarase incorrectamente por mor dos caracteres especiais.
A conclusión sobre as cordas é sinxela: non traballes con cordas e non confíes en cordas dentro do contrato. Se é importante gardar cadeas, garda bytes e confía en bytes e converte cadeas en bytes no propio servizo.
A seguinte complexidade, que é unha extensión da característica principal da cadea de bloques, é o illamento. Todos os datos que residen na cadea de bloques nacen dentro da cadea de bloques ou se transmiten a ela desde o exterior. Pero a propia cadea de bloques nunca pode tocar o mundo exterior, só outros contratos intelixentes.
O problema é que todos os comandos de contrato intelixente execútanse en cada participante da rede. E non pode confiar nunha fonte externa, xa que non pode estar seguro de que se reciba a mesma información en todos os nodos. Ocorrerá que cada nodo terá unha versión diferente da cadea de bloques con datos diferentes e a cadea de bloques colapsará.
E a trivial tarefa de "sacar a temperatura actual fóra" convértese en algo imposible. Aínda que non sempre necesitamos o tempo, algúns datos (como os tipos de cambio ou o estado actual dalgún sistema externo) son esenciais. A solución reside no seguinte enfoque:
Resulta que é unha cadea tan longa. A tristeza da historia é que o diñeiro tómase na miña primeira solicitude do formulario "vai por min en tal petición" e na segunda solicitude, que xa o fai o servidor que executou a solicitude.
Por exemplo, lévanos 50k de gas por cada paso. Comezamos a transacción, poñemos 50k GAS LIMIT e pensamos que iremos ben. Pero, por exemplo, a mecánica de gardar o novo tempo cambia: agora, cando a temperatura supera os 10 graos, necesitamos transferir diñeiro a un dos participantes. A lóxica se expande, e agora levará, por exemplo, 80k de gas por transacción.
Ao final, xa na segunda transacción, toda a cadea colapsa por falta de gas para a transacción. Tal "horta" arredor das convocatorias externas fai que estes proxectos sexan máis complicados. O máis probable é que se tes unha conexión dura co mundo exterior, non deberías escoller blockchain para o teu proxecto.
Tampouco hai unha aleatoriedade normal que non se poida predeterminar. Esta aleatoriedade tamén é proporcionada por diferentes provedores "tal como está": só se escribe regularmente un valor aleatorio no contrato intelixente. Pero é perigoso confiar en tal cousa para proxectos financeiros reais.
O feito de que o valor da variable block.timestamp
sexa establecido polo mineiro de bloques merece unha atención especial. Por suposto, é difícil imaxinar que o mineiro saberá de antemán que é el quen extrae o bloque, e pode substituír o tempo. Aínda así, hai unha posibilidade hipotética. Este perigo é relevante no contexto de 15 segundos, e se confiamos en minutos e grandes intervalos de tempo, non hai tal problema.
Non penso falar moito de seguridade. Pero vou facer fincapé nun aspecto importante: todo o que hai na cadea de bloques é visible para todos. O único que é inaccesible para outros é a túa chave privada. O código de contrato intelixente pásase ao aberto para pasar auditorías e para que os usuarios de contratos intelixentes poidan confiar nel.
O procedemento de auditoría significa que unha empresa está contratada para ver o código do contrato intelixente e verificar que este contrato en particular estea publicado neste enderezo. Compróbase a cuestión da seguridade do contrato e fai o que declaran os desenvolvedores. A continuación, a empresa de auditoría publica información como "este contrato foi verificado por nós - pódese confiar" no seu sitio web.
Pero aínda que non se proporcione o código do contrato, pódese descompilar facilmente. Por exemplo, o seguinte código ten unha variable inmutable: simplemente substitúese en todas partes por unha constante no código a continuación.
Despois de implementar este contrato e abrilo a través do descompilador, vemos o seguinte:
É dicir, obtemos este valor da variable inmediatamente.
Estou afeito a poder estar tranquilo no backend, e o valor das variables privadas para ler sen acceso á memoria será problemático. Aquí pasa o mesmo: todo o mundo ten acceso á "memoria".
Chamámoslle privada á amount
variable. Implementa o contrato intelixente e, a continuación, extrae o seu valor cun simple fragmento de código:
Podes acabar sacando calquera cousa deste xeito. Polo tanto, non penses en gardar nada sensible no contrato intelixente.
É basicamente imposible revertir os cambios. Un contrato intelixente desasignase unha vez e non se pode cambiar nada. Permanecerá na cadea de bloques ata o final dos tempos e despois algún.
Por iso hai que escribir todo correctamente e ben á vez. Non podo facelo, así que axiña se me ocorreu unha solución alternativa interesante: Contratos actualizables . A súa mecánica funciona do seguinte xeito.
A primeira versión do contrato (Contract V1) está publicada
Se publica un contrato de proxy e ten a seguinte tarefa: reenviar todas as solicitudes 1v1 ao contrato V1 ou usar o seu propio almacenamento e usar só a lóxica do contrato de destino.
Ademais o usuario comunícase co contrato de proxy da mesma forma que co principal.
Se é necesario actualizar o contrato, o administrador desprega o Contrato V2 e, a través do contrato de administración, indica a contrato de proxy que a implementación está agora no enderezo do Contrato V2.
A continuación, o usuario tamén se comunica co proxy e as mecánicas do Contract V2 xa están executadas.
A continuación, o usuario tamén se comunica co proxy e a mecánica do Contract V2 xa está executada.
Este mecanismo ten unha serie de limitacións e trucos. Por exemplo, as variables da versión anterior non se poden cambiar na nova versión do contrato. Se unha variable xa non é necesaria, aínda debe deixarse e despoboar no novo contrato.
Por suposto, esta solución e moitas outras xa teñen solucións preparadas. O principal provedor destes desenvolvementos é OpenZeppelin . Así que, afortunadamente, non hai que reinventar a roda.
Contrato actualizable:
Os contratos intelixentes actualizables son un gran motivo para non ser auditados. O mundo da cadea de bloques está construído na confianza. Agora, un contrato intelixente pode ter unha mecánica honesta e aberta, pero máis tarde, o propietario do contrato intelixente cambiará a implementación a outra onde leva todo o diñeiro.