diff --git a/README.md b/README.md index f7a5df4b..40639994 100644 --- a/README.md +++ b/README.md @@ -118,9 +118,10 @@ You can use `new-mnemonic --help` to see all arguments. Note that if there are m | Argument | Type | Description | | -------- | -------- | -------- | | `--num_validators` | Non-negative integer | The number of signing keys you want to generate. Note that the child key(s) are generated via the same master key. | -| `--mnemonic_language` | String. Options: `czech`, `chinese_traditional`, `chinese_simplified`, `english`, `spanish`, `italian`, `korean`. Default to `english` | The mnemonic language | +| `--mnemonic_language` | String. Options: `chinese_simplified`, `chinese_traditional`, `czech`, `english`, `italian`, `korean`, `portuguese`, `spanish`. Default to `english` | The mnemonic language | | `--folder` | String. Pointing to `./validator_keys` by default | The folder path for the keystore(s) and deposit(s) | | `--chain` | String. `mainnet` by default | The chain setting for the signing domain. | +| `--eth1_withdrawal_address` | String. Eth1 address in hexadecimal encoded form | If this field is set and valid, the given Eth1 address will be used to create the withdrawal credentials. Otherwise, it will generate withdrawal credentials with the mnemonic-derived withdrawal public key in [EIP-2334 format](https://eips.ethereum.org/EIPS/eip-2334#eth2-specific-parameters). | ###### `existing-mnemonic` Arguments @@ -132,6 +133,7 @@ You can use `existing-mnemonic --help` to see all arguments. Note that if there | `--num_validators` | Non-negative integer | The number of signing keys you want to generate. Note that the child key(s) are generated via the same master key. | | `--folder` | String. Pointing to `./validator_keys` by default | The folder path for the keystore(s) and deposit(s) | | `--chain` | String. `mainnet` by default | The chain setting for the signing domain. | +| `--eth1_withdrawal_address` | String. Eth1 address in hexadecimal encoded form | If this field is set and valid, the given Eth1 address will be used to create the withdrawal credentials. Otherwise, it will generate withdrawal credentials with the mnemonic-derived withdrawal public key in [EIP-2334 format](https://eips.ethereum.org/EIPS/eip-2334#eth2-specific-parameters). | ###### Successful message diff --git a/eth2deposit/cli/generate_keys.py b/eth2deposit/cli/generate_keys.py index 8b85b3e1..4c12c3d9 100644 --- a/eth2deposit/cli/generate_keys.py +++ b/eth2deposit/cli/generate_keys.py @@ -5,6 +5,9 @@ Callable, ) +from eth_typing import HexAddress +from eth_utils import is_hex_address, to_normalized_address + from eth2deposit.credentials import ( CredentialList, ) @@ -57,6 +60,18 @@ def validate_password(cts: click.Context, param: Any, password: str) -> str: return password +def validate_eth1_withdrawal_address(cts: click.Context, param: Any, address: str) -> HexAddress: + if address is None: + return None + if not is_hex_address(address): + raise ValueError("The given Eth1 address is not in hexadecimal encoded form.") + + normalized_address = to_normalized_address(address) + click.echo(f'\n**[Warning] you are setting Eth1 address {normalized_address} as your withdrawal address. ' + 'Please ensure that you have control over this address.**\n') + return normalized_address + + def generate_keys_arguments_decorator(function: Callable[..., Any]) -> Callable[..., Any]: ''' This is a decorator that, when applied to a parent-command, implements the @@ -91,6 +106,14 @@ def generate_keys_arguments_decorator(function: Callable[..., Any]) -> Callable[ 'to ask you for your mnemonic as otherwise it will appear in your shell history.)'), prompt='Type the password that secures your validator keystore(s)', ), + click.option( + '--eth1_withdrawal_address', + default=None, + callback=validate_eth1_withdrawal_address, + help=('If this field is set and valid, the given Eth1 address will be used to create the ' + 'withdrawal credentials. Otherwise, it will generate withdrawal credentials with the ' + 'mnemonic-derived withdrawal public key.'), + ), ] for decorator in reversed(decorators): function = decorator(function) @@ -100,7 +123,8 @@ def generate_keys_arguments_decorator(function: Callable[..., Any]) -> Callable[ @click.command() @click.pass_context def generate_keys(ctx: click.Context, validator_start_index: int, - num_validators: int, folder: str, chain: str, keystore_password: str, **kwargs: Any) -> None: + num_validators: int, folder: str, chain: str, keystore_password: str, + eth1_withdrawal_address: HexAddress, **kwargs: Any) -> None: mnemonic = ctx.obj['mnemonic'] mnemonic_password = ctx.obj['mnemonic_password'] amounts = [MAX_DEPOSIT_AMOUNT] * num_validators @@ -118,12 +142,13 @@ def generate_keys(ctx: click.Context, validator_start_index: int, amounts=amounts, chain_setting=chain_setting, start_index=validator_start_index, + hex_eth1_withdrawal_address=eth1_withdrawal_address, ) keystore_filefolders = credentials.export_keystores(password=keystore_password, folder=folder) deposits_file = credentials.export_deposit_data_json(folder=folder) if not credentials.verify_keystores(keystore_filefolders=keystore_filefolders, password=keystore_password): raise ValidationError("Failed to verify the keystores.") - if not verify_deposit_data_json(deposits_file): + if not verify_deposit_data_json(deposits_file, credentials.credentials): raise ValidationError("Failed to verify the deposit data JSON files.") click.echo('\nSuccess!\nYour keys can be found at: %s' % folder) click.pause('\n\nPress any key.') diff --git a/eth2deposit/credentials.py b/eth2deposit/credentials.py index a7d864af..c76b26d0 100644 --- a/eth2deposit/credentials.py +++ b/eth2deposit/credentials.py @@ -1,8 +1,12 @@ import os import click +from enum import Enum import time import json -from typing import Dict, List +from typing import Dict, List, Optional + +from eth_typing import Address, HexAddress +from eth_utils import to_canonical_address from py_ecc.bls import G2ProofOfPossession as bls from eth2deposit.exceptions import ValidationError @@ -14,6 +18,7 @@ from eth2deposit.settings import DEPOSIT_CLI_VERSION, BaseChainSetting from eth2deposit.utils.constants import ( BLS_WITHDRAWAL_PREFIX, + ETH1_ADDRESS_WITHDRAWAL_PREFIX, ETH2GWEI, MAX_DEPOSIT_AMOUNT, MIN_DEPOSIT_AMOUNT, @@ -27,13 +32,19 @@ ) +class WithdrawalType(Enum): + BLS_WITHDRAWAL = 0 + ETH1_ADDRESS_WITHDRAWAL = 1 + + class Credential: """ A Credential object contains all of the information for a single validator and the corresponding functionality. Once created, it is the only object that should be required to perform any processing for a validator. """ def __init__(self, *, mnemonic: str, mnemonic_password: str, - index: int, amount: int, chain_setting: BaseChainSetting): + index: int, amount: int, chain_setting: BaseChainSetting, + hex_eth1_withdrawal_address: Optional[HexAddress]): # Set path as EIP-2334 format # https://eips.ethereum.org/EIPS/eip-2334 purpose = '12381' @@ -48,6 +59,7 @@ def __init__(self, *, mnemonic: str, mnemonic_password: str, mnemonic=mnemonic, path=self.signing_key_path, password=mnemonic_password) self.amount = amount self.chain_setting = chain_setting + self.hex_eth1_withdrawal_address = hex_eth1_withdrawal_address @property def signing_pk(self) -> bytes: @@ -57,10 +69,42 @@ def signing_pk(self) -> bytes: def withdrawal_pk(self) -> bytes: return bls.SkToPk(self.withdrawal_sk) + @property + def eth1_withdrawal_address(self) -> Optional[Address]: + if self.hex_eth1_withdrawal_address is None: + return None + return to_canonical_address(self.hex_eth1_withdrawal_address) + + @property + def withdrawal_prefix(self) -> bytes: + if self.eth1_withdrawal_address is not None: + return ETH1_ADDRESS_WITHDRAWAL_PREFIX + else: + return BLS_WITHDRAWAL_PREFIX + + @property + def withdrawal_type(self) -> WithdrawalType: + if self.withdrawal_prefix == BLS_WITHDRAWAL_PREFIX: + return WithdrawalType.BLS_WITHDRAWAL + elif self.withdrawal_prefix == ETH1_ADDRESS_WITHDRAWAL_PREFIX: + return WithdrawalType.ETH1_ADDRESS_WITHDRAWAL + else: + raise ValueError(f"Invalid withdrawal_prefix {self.withdrawal_prefix.hex()}") + @property def withdrawal_credentials(self) -> bytes: - withdrawal_credentials = BLS_WITHDRAWAL_PREFIX - withdrawal_credentials += SHA256(self.withdrawal_pk)[1:] + if self.withdrawal_type == WithdrawalType.BLS_WITHDRAWAL: + withdrawal_credentials = BLS_WITHDRAWAL_PREFIX + withdrawal_credentials += SHA256(self.withdrawal_pk)[1:] + elif ( + self.withdrawal_type == WithdrawalType.ETH1_ADDRESS_WITHDRAWAL + and self.eth1_withdrawal_address is not None + ): + withdrawal_credentials = ETH1_ADDRESS_WITHDRAWAL_PREFIX + withdrawal_credentials += b'\x00' * 11 + withdrawal_credentials += self.eth1_withdrawal_address + else: + raise ValueError(f"Invalid withdrawal_type {self.withdrawal_type}") return withdrawal_credentials @property @@ -129,7 +173,8 @@ def from_mnemonic(cls, num_keys: int, amounts: List[int], chain_setting: BaseChainSetting, - start_index: int) -> 'CredentialList': + start_index: int, + hex_eth1_withdrawal_address: Optional[HexAddress]) -> 'CredentialList': if len(amounts) != num_keys: raise ValueError( f"The number of keys ({num_keys}) doesn't equal to the corresponding deposit amounts ({len(amounts)})." @@ -138,7 +183,8 @@ def from_mnemonic(cls, with click.progressbar(key_indices, label='Creating your keys:\t\t', show_percent=False, show_pos=True) as indices: return cls([Credential(mnemonic=mnemonic, mnemonic_password=mnemonic_password, - index=index, amount=amounts[index - start_index], chain_setting=chain_setting) + index=index, amount=amounts[index - start_index], chain_setting=chain_setting, + hex_eth1_withdrawal_address=hex_eth1_withdrawal_address) for index in indices]) def export_keystores(self, password: str, folder: str) -> List[str]: diff --git a/eth2deposit/key_handling/key_derivation/word_lists/portuguese.txt b/eth2deposit/key_handling/key_derivation/word_lists/portuguese.txt new file mode 100644 index 00000000..4a891055 --- /dev/null +++ b/eth2deposit/key_handling/key_derivation/word_lists/portuguese.txt @@ -0,0 +1,2048 @@ +abacate +abaixo +abalar +abater +abduzir +abelha +aberto +abismo +abotoar +abranger +abreviar +abrigar +abrupto +absinto +absoluto +absurdo +abutre +acabado +acalmar +acampar +acanhar +acaso +aceitar +acelerar +acenar +acervo +acessar +acetona +achatar +acidez +acima +acionado +acirrar +aclamar +aclive +acolhida +acomodar +acoplar +acordar +acumular +acusador +adaptar +adega +adentro +adepto +adequar +aderente +adesivo +adeus +adiante +aditivo +adjetivo +adjunto +admirar +adorar +adquirir +adubo +adverso +advogado +aeronave +afastar +aferir +afetivo +afinador +afivelar +aflito +afluente +afrontar +agachar +agarrar +agasalho +agenciar +agilizar +agiota +agitado +agora +agradar +agreste +agrupar +aguardar +agulha +ajoelhar +ajudar +ajustar +alameda +alarme +alastrar +alavanca +albergue +albino +alcatra +aldeia +alecrim +alegria +alertar +alface +alfinete +algum +alheio +aliar +alicate +alienar +alinhar +aliviar +almofada +alocar +alpiste +alterar +altitude +alucinar +alugar +aluno +alusivo +alvo +amaciar +amador +amarelo +amassar +ambas +ambiente +ameixa +amenizar +amido +amistoso +amizade +amolador +amontoar +amoroso +amostra +amparar +ampliar +ampola +anagrama +analisar +anarquia +anatomia +andaime +anel +anexo +angular +animar +anjo +anomalia +anotado +ansioso +anterior +anuidade +anunciar +anzol +apagador +apalpar +apanhado +apego +apelido +apertada +apesar +apetite +apito +aplauso +aplicada +apoio +apontar +aposta +aprendiz +aprovar +aquecer +arame +aranha +arara +arcada +ardente +areia +arejar +arenito +aresta +argiloso +argola +arma +arquivo +arraial +arrebate +arriscar +arroba +arrumar +arsenal +arterial +artigo +arvoredo +asfaltar +asilado +aspirar +assador +assinar +assoalho +assunto +astral +atacado +atadura +atalho +atarefar +atear +atender +aterro +ateu +atingir +atirador +ativo +atoleiro +atracar +atrevido +atriz +atual +atum +auditor +aumentar +aura +aurora +autismo +autoria +autuar +avaliar +avante +avaria +avental +avesso +aviador +avisar +avulso +axila +azarar +azedo +azeite +azulejo +babar +babosa +bacalhau +bacharel +bacia +bagagem +baiano +bailar +baioneta +bairro +baixista +bajular +baleia +baliza +balsa +banal +bandeira +banho +banir +banquete +barato +barbado +baronesa +barraca +barulho +baseado +bastante +batata +batedor +batida +batom +batucar +baunilha +beber +beijo +beirada +beisebol +beldade +beleza +belga +beliscar +bendito +bengala +benzer +berimbau +berlinda +berro +besouro +bexiga +bezerro +bico +bicudo +bienal +bifocal +bifurcar +bigorna +bilhete +bimestre +bimotor +biologia +biombo +biosfera +bipolar +birrento +biscoito +bisneto +bispo +bissexto +bitola +bizarro +blindado +bloco +bloquear +boato +bobagem +bocado +bocejo +bochecha +boicotar +bolada +boletim +bolha +bolo +bombeiro +bonde +boneco +bonita +borbulha +borda +boreal +borracha +bovino +boxeador +branco +brasa +braveza +breu +briga +brilho +brincar +broa +brochura +bronzear +broto +bruxo +bucha +budismo +bufar +bule +buraco +busca +busto +buzina +cabana +cabelo +cabide +cabo +cabrito +cacau +cacetada +cachorro +cacique +cadastro +cadeado +cafezal +caiaque +caipira +caixote +cajado +caju +calafrio +calcular +caldeira +calibrar +calmante +calota +camada +cambista +camisa +camomila +campanha +camuflar +canavial +cancelar +caneta +canguru +canhoto +canivete +canoa +cansado +cantar +canudo +capacho +capela +capinar +capotar +capricho +captador +capuz +caracol +carbono +cardeal +careca +carimbar +carneiro +carpete +carreira +cartaz +carvalho +casaco +casca +casebre +castelo +casulo +catarata +cativar +caule +causador +cautelar +cavalo +caverna +cebola +cedilha +cegonha +celebrar +celular +cenoura +censo +centeio +cercar +cerrado +certeiro +cerveja +cetim +cevada +chacota +chaleira +chamado +chapada +charme +chatice +chave +chefe +chegada +cheiro +cheque +chicote +chifre +chinelo +chocalho +chover +chumbo +chutar +chuva +cicatriz +ciclone +cidade +cidreira +ciente +cigana +cimento +cinto +cinza +ciranda +circuito +cirurgia +citar +clareza +clero +clicar +clone +clube +coado +coagir +cobaia +cobertor +cobrar +cocada +coelho +coentro +coeso +cogumelo +coibir +coifa +coiote +colar +coleira +colher +colidir +colmeia +colono +coluna +comando +combinar +comentar +comitiva +comover +complexo +comum +concha +condor +conectar +confuso +congelar +conhecer +conjugar +consumir +contrato +convite +cooperar +copeiro +copiador +copo +coquetel +coragem +cordial +corneta +coronha +corporal +correio +cortejo +coruja +corvo +cosseno +costela +cotonete +couro +couve +covil +cozinha +cratera +cravo +creche +credor +creme +crer +crespo +criada +criminal +crioulo +crise +criticar +crosta +crua +cruzeiro +cubano +cueca +cuidado +cujo +culatra +culminar +culpar +cultura +cumprir +cunhado +cupido +curativo +curral +cursar +curto +cuspir +custear +cutelo +damasco +datar +debater +debitar +deboche +debulhar +decalque +decimal +declive +decote +decretar +dedal +dedicado +deduzir +defesa +defumar +degelo +degrau +degustar +deitado +deixar +delator +delegado +delinear +delonga +demanda +demitir +demolido +dentista +depenado +depilar +depois +depressa +depurar +deriva +derramar +desafio +desbotar +descanso +desenho +desfiado +desgaste +desigual +deslize +desmamar +desova +despesa +destaque +desviar +detalhar +detentor +detonar +detrito +deusa +dever +devido +devotado +dezena +diagrama +dialeto +didata +difuso +digitar +dilatado +diluente +diminuir +dinastia +dinheiro +diocese +direto +discreta +disfarce +disparo +disquete +dissipar +distante +ditador +diurno +diverso +divisor +divulgar +dizer +dobrador +dolorido +domador +dominado +donativo +donzela +dormente +dorsal +dosagem +dourado +doutor +drenagem +drible +drogaria +duelar +duende +dueto +duplo +duquesa +durante +duvidoso +eclodir +ecoar +ecologia +edificar +edital +educado +efeito +efetivar +ejetar +elaborar +eleger +eleitor +elenco +elevador +eliminar +elogiar +embargo +embolado +embrulho +embutido +emenda +emergir +emissor +empatia +empenho +empinado +empolgar +emprego +empurrar +emulador +encaixe +encenado +enchente +encontro +endeusar +endossar +enfaixar +enfeite +enfim +engajado +engenho +englobar +engomado +engraxar +enguia +enjoar +enlatar +enquanto +enraizar +enrolado +enrugar +ensaio +enseada +ensino +ensopado +entanto +enteado +entidade +entortar +entrada +entulho +envergar +enviado +envolver +enxame +enxerto +enxofre +enxuto +epiderme +equipar +ereto +erguido +errata +erva +ervilha +esbanjar +esbelto +escama +escola +escrita +escuta +esfinge +esfolar +esfregar +esfumado +esgrima +esmalte +espanto +espelho +espiga +esponja +espreita +espumar +esquerda +estaca +esteira +esticar +estofado +estrela +estudo +esvaziar +etanol +etiqueta +euforia +europeu +evacuar +evaporar +evasivo +eventual +evidente +evoluir +exagero +exalar +examinar +exato +exausto +excesso +excitar +exclamar +executar +exemplo +exibir +exigente +exonerar +expandir +expelir +expirar +explanar +exposto +expresso +expulsar +externo +extinto +extrato +fabricar +fabuloso +faceta +facial +fada +fadiga +faixa +falar +falta +familiar +fandango +fanfarra +fantoche +fardado +farelo +farinha +farofa +farpa +fartura +fatia +fator +favorita +faxina +fazenda +fechado +feijoada +feirante +felino +feminino +fenda +feno +fera +feriado +ferrugem +ferver +festejar +fetal +feudal +fiapo +fibrose +ficar +ficheiro +figurado +fileira +filho +filme +filtrar +firmeza +fisgada +fissura +fita +fivela +fixador +fixo +flacidez +flamingo +flanela +flechada +flora +flutuar +fluxo +focal +focinho +fofocar +fogo +foguete +foice +folgado +folheto +forjar +formiga +forno +forte +fosco +fossa +fragata +fralda +frango +frasco +fraterno +freira +frente +fretar +frieza +friso +fritura +fronha +frustrar +fruteira +fugir +fulano +fuligem +fundar +fungo +funil +furador +furioso +futebol +gabarito +gabinete +gado +gaiato +gaiola +gaivota +galega +galho +galinha +galocha +ganhar +garagem +garfo +gargalo +garimpo +garoupa +garrafa +gasoduto +gasto +gata +gatilho +gaveta +gazela +gelado +geleia +gelo +gemada +gemer +gemido +generoso +gengiva +genial +genoma +genro +geologia +gerador +germinar +gesso +gestor +ginasta +gincana +gingado +girafa +girino +glacial +glicose +global +glorioso +goela +goiaba +golfe +golpear +gordura +gorjeta +gorro +gostoso +goteira +governar +gracejo +gradual +grafite +gralha +grampo +granada +gratuito +graveto +graxa +grego +grelhar +greve +grilo +grisalho +gritaria +grosso +grotesco +grudado +grunhido +gruta +guache +guarani +guaxinim +guerrear +guiar +guincho +guisado +gula +guloso +guru +habitar +harmonia +haste +haver +hectare +herdar +heresia +hesitar +hiato +hibernar +hidratar +hiena +hino +hipismo +hipnose +hipoteca +hoje +holofote +homem +honesto +honrado +hormonal +hospedar +humorado +iate +ideia +idoso +ignorado +igreja +iguana +ileso +ilha +iludido +iluminar +ilustrar +imagem +imediato +imenso +imersivo +iminente +imitador +imortal +impacto +impedir +implante +impor +imprensa +impune +imunizar +inalador +inapto +inativo +incenso +inchar +incidir +incluir +incolor +indeciso +indireto +indutor +ineficaz +inerente +infantil +infestar +infinito +inflamar +informal +infrator +ingerir +inibido +inicial +inimigo +injetar +inocente +inodoro +inovador +inox +inquieto +inscrito +inseto +insistir +inspetor +instalar +insulto +intacto +integral +intimar +intocado +intriga +invasor +inverno +invicto +invocar +iogurte +iraniano +ironizar +irreal +irritado +isca +isento +isolado +isqueiro +italiano +janeiro +jangada +janta +jararaca +jardim +jarro +jasmim +jato +javali +jazida +jejum +joaninha +joelhada +jogador +joia +jornal +jorrar +jovem +juba +judeu +judoca +juiz +julgador +julho +jurado +jurista +juro +justa +labareda +laboral +lacre +lactante +ladrilho +lagarta +lagoa +laje +lamber +lamentar +laminar +lampejo +lanche +lapidar +lapso +laranja +lareira +largura +lasanha +lastro +lateral +latido +lavanda +lavoura +lavrador +laxante +lazer +lealdade +lebre +legado +legendar +legista +leigo +leiloar +leitura +lembrete +leme +lenhador +lentilha +leoa +lesma +leste +letivo +letreiro +levar +leveza +levitar +liberal +libido +liderar +ligar +ligeiro +limitar +limoeiro +limpador +linda +linear +linhagem +liquidez +listagem +lisura +litoral +livro +lixa +lixeira +locador +locutor +lojista +lombo +lona +longe +lontra +lorde +lotado +loteria +loucura +lousa +louvar +luar +lucidez +lucro +luneta +lustre +lutador +luva +macaco +macete +machado +macio +madeira +madrinha +magnata +magreza +maior +mais +malandro +malha +malote +maluco +mamilo +mamoeiro +mamute +manada +mancha +mandato +manequim +manhoso +manivela +manobrar +mansa +manter +manusear +mapeado +maquinar +marcador +maresia +marfim +margem +marinho +marmita +maroto +marquise +marreco +martelo +marujo +mascote +masmorra +massagem +mastigar +matagal +materno +matinal +matutar +maxilar +medalha +medida +medusa +megafone +meiga +melancia +melhor +membro +memorial +menino +menos +mensagem +mental +merecer +mergulho +mesada +mesclar +mesmo +mesquita +mestre +metade +meteoro +metragem +mexer +mexicano +micro +migalha +migrar +milagre +milenar +milhar +mimado +minerar +minhoca +ministro +minoria +miolo +mirante +mirtilo +misturar +mocidade +moderno +modular +moeda +moer +moinho +moita +moldura +moleza +molho +molinete +molusco +montanha +moqueca +morango +morcego +mordomo +morena +mosaico +mosquete +mostarda +motel +motim +moto +motriz +muda +muito +mulata +mulher +multar +mundial +munido +muralha +murcho +muscular +museu +musical +nacional +nadador +naja +namoro +narina +narrado +nascer +nativa +natureza +navalha +navegar +navio +neblina +nebuloso +negativa +negociar +negrito +nervoso +neta +neural +nevasca +nevoeiro +ninar +ninho +nitidez +nivelar +nobreza +noite +noiva +nomear +nominal +nordeste +nortear +notar +noticiar +noturno +novelo +novilho +novo +nublado +nudez +numeral +nupcial +nutrir +nuvem +obcecado +obedecer +objetivo +obrigado +obscuro +obstetra +obter +obturar +ocidente +ocioso +ocorrer +oculista +ocupado +ofegante +ofensiva +oferenda +oficina +ofuscado +ogiva +olaria +oleoso +olhar +oliveira +ombro +omelete +omisso +omitir +ondulado +oneroso +ontem +opcional +operador +oponente +oportuno +oposto +orar +orbitar +ordem +ordinal +orfanato +orgasmo +orgulho +oriental +origem +oriundo +orla +ortodoxo +orvalho +oscilar +ossada +osso +ostentar +otimismo +ousadia +outono +outubro +ouvido +ovelha +ovular +oxidar +oxigenar +pacato +paciente +pacote +pactuar +padaria +padrinho +pagar +pagode +painel +pairar +paisagem +palavra +palestra +palheta +palito +palmada +palpitar +pancada +panela +panfleto +panqueca +pantanal +papagaio +papelada +papiro +parafina +parcial +pardal +parede +partida +pasmo +passado +pastel +patamar +patente +patinar +patrono +paulada +pausar +peculiar +pedalar +pedestre +pediatra +pedra +pegada +peitoral +peixe +pele +pelicano +penca +pendurar +peneira +penhasco +pensador +pente +perceber +perfeito +pergunta +perito +permitir +perna +perplexo +persiana +pertence +peruca +pescado +pesquisa +pessoa +petiscar +piada +picado +piedade +pigmento +pilastra +pilhado +pilotar +pimenta +pincel +pinguim +pinha +pinote +pintar +pioneiro +pipoca +piquete +piranha +pires +pirueta +piscar +pistola +pitanga +pivete +planta +plaqueta +platina +plebeu +plumagem +pluvial +pneu +poda +poeira +poetisa +polegada +policiar +poluente +polvilho +pomar +pomba +ponderar +pontaria +populoso +porta +possuir +postal +pote +poupar +pouso +povoar +praia +prancha +prato +praxe +prece +predador +prefeito +premiar +prensar +preparar +presilha +pretexto +prevenir +prezar +primata +princesa +prisma +privado +processo +produto +profeta +proibido +projeto +prometer +propagar +prosa +protetor +provador +publicar +pudim +pular +pulmonar +pulseira +punhal +punir +pupilo +pureza +puxador +quadra +quantia +quarto +quase +quebrar +queda +queijo +quente +querido +quimono +quina +quiosque +rabanada +rabisco +rachar +racionar +radial +raiar +rainha +raio +raiva +rajada +ralado +ramal +ranger +ranhura +rapadura +rapel +rapidez +raposa +raquete +raridade +rasante +rascunho +rasgar +raspador +rasteira +rasurar +ratazana +ratoeira +realeza +reanimar +reaver +rebaixar +rebelde +rebolar +recado +recente +recheio +recibo +recordar +recrutar +recuar +rede +redimir +redonda +reduzida +reenvio +refinar +refletir +refogar +refresco +refugiar +regalia +regime +regra +reinado +reitor +rejeitar +relativo +remador +remendo +remorso +renovado +reparo +repelir +repleto +repolho +represa +repudiar +requerer +resenha +resfriar +resgatar +residir +resolver +respeito +ressaca +restante +resumir +retalho +reter +retirar +retomada +retratar +revelar +revisor +revolta +riacho +rica +rigidez +rigoroso +rimar +ringue +risada +risco +risonho +robalo +rochedo +rodada +rodeio +rodovia +roedor +roleta +romano +roncar +rosado +roseira +rosto +rota +roteiro +rotina +rotular +rouco +roupa +roxo +rubro +rugido +rugoso +ruivo +rumo +rupestre +russo +sabor +saciar +sacola +sacudir +sadio +safira +saga +sagrada +saibro +salada +saleiro +salgado +saliva +salpicar +salsicha +saltar +salvador +sambar +samurai +sanar +sanfona +sangue +sanidade +sapato +sarda +sargento +sarjeta +saturar +saudade +saxofone +sazonal +secar +secular +seda +sedento +sediado +sedoso +sedutor +segmento +segredo +segundo +seiva +seleto +selvagem +semanal +semente +senador +senhor +sensual +sentado +separado +sereia +seringa +serra +servo +setembro +setor +sigilo +silhueta +silicone +simetria +simpatia +simular +sinal +sincero +singular +sinopse +sintonia +sirene +siri +situado +soberano +sobra +socorro +sogro +soja +solda +soletrar +solteiro +sombrio +sonata +sondar +sonegar +sonhador +sono +soprano +soquete +sorrir +sorteio +sossego +sotaque +soterrar +sovado +sozinho +suavizar +subida +submerso +subsolo +subtrair +sucata +sucesso +suco +sudeste +sufixo +sugador +sugerir +sujeito +sulfato +sumir +suor +superior +suplicar +suposto +suprimir +surdina +surfista +surpresa +surreal +surtir +suspiro +sustento +tabela +tablete +tabuada +tacho +tagarela +talher +talo +talvez +tamanho +tamborim +tampa +tangente +tanto +tapar +tapioca +tardio +tarefa +tarja +tarraxa +tatuagem +taurino +taxativo +taxista +teatral +tecer +tecido +teclado +tedioso +teia +teimar +telefone +telhado +tempero +tenente +tensor +tentar +termal +terno +terreno +tese +tesoura +testado +teto +textura +texugo +tiara +tigela +tijolo +timbrar +timidez +tingido +tinteiro +tiragem +titular +toalha +tocha +tolerar +tolice +tomada +tomilho +tonel +tontura +topete +tora +torcido +torneio +torque +torrada +torto +tostar +touca +toupeira +toxina +trabalho +tracejar +tradutor +trafegar +trajeto +trama +trancar +trapo +traseiro +tratador +travar +treino +tremer +trepidar +trevo +triagem +tribo +triciclo +tridente +trilogia +trindade +triplo +triturar +triunfal +trocar +trombeta +trova +trunfo +truque +tubular +tucano +tudo +tulipa +tupi +turbo +turma +turquesa +tutelar +tutorial +uivar +umbigo +unha +unidade +uniforme +urologia +urso +urtiga +urubu +usado +usina +usufruir +vacina +vadiar +vagaroso +vaidoso +vala +valente +validade +valores +vantagem +vaqueiro +varanda +vareta +varrer +vascular +vasilha +vassoura +vazar +vazio +veado +vedar +vegetar +veicular +veleiro +velhice +veludo +vencedor +vendaval +venerar +ventre +verbal +verdade +vereador +vergonha +vermelho +verniz +versar +vertente +vespa +vestido +vetorial +viaduto +viagem +viajar +viatura +vibrador +videira +vidraria +viela +viga +vigente +vigiar +vigorar +vilarejo +vinco +vinheta +vinil +violeta +virada +virtude +visitar +visto +vitral +viveiro +vizinho +voador +voar +vogal +volante +voleibol +voltagem +volumoso +vontade +vulto +vuvuzela +xadrez +xarope +xeque +xeretar +xerife +xingar +zangado +zarpar +zebu +zelador +zombar +zoologia +zumbido diff --git a/eth2deposit/settings.py b/eth2deposit/settings.py index 8558afe4..d1c185ae 100644 --- a/eth2deposit/settings.py +++ b/eth2deposit/settings.py @@ -1,7 +1,7 @@ from typing import Dict, NamedTuple -DEPOSIT_CLI_VERSION = '1.1.1' +DEPOSIT_CLI_VERSION = '1.2.0' class BaseChainSetting(NamedTuple): diff --git a/eth2deposit/utils/constants.py b/eth2deposit/utils/constants.py index a0f40a5d..13d1d629 100644 --- a/eth2deposit/utils/constants.py +++ b/eth2deposit/utils/constants.py @@ -6,6 +6,7 @@ # Eth2-spec constants taken from https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md DOMAIN_DEPOSIT = bytes.fromhex('03000000') BLS_WITHDRAWAL_PREFIX = bytes.fromhex('00') +ETH1_ADDRESS_WITHDRAWAL_PREFIX = bytes.fromhex('01') ETH2GWEI = 10 ** 9 MIN_DEPOSIT_AMOUNT = 2 ** 0 * ETH2GWEI diff --git a/eth2deposit/utils/validation.py b/eth2deposit/utils/validation.py index 96b8d5ca..23a513d8 100644 --- a/eth2deposit/utils/validation.py +++ b/eth2deposit/utils/validation.py @@ -4,7 +4,7 @@ BLSPubkey, BLSSignature, ) -from typing import Any, Dict +from typing import Any, Dict, Sequence from py_ecc.bls import G2ProofOfPossession as bls @@ -15,13 +15,19 @@ DepositData, DepositMessage, ) +from eth2deposit.credentials import ( + Credential, +) from eth2deposit.utils.constants import ( MAX_DEPOSIT_AMOUNT, MIN_DEPOSIT_AMOUNT, + BLS_WITHDRAWAL_PREFIX, + ETH1_ADDRESS_WITHDRAWAL_PREFIX, ) +from eth2deposit.utils.crypto import SHA256 -def verify_deposit_data_json(filefolder: str) -> bool: +def verify_deposit_data_json(filefolder: str, credentials: Sequence[Credential]) -> bool: """ Validate every deposit found in the deposit-data JSON file folder. """ @@ -29,11 +35,11 @@ def verify_deposit_data_json(filefolder: str) -> bool: deposit_json = json.load(f) with click.progressbar(deposit_json, label='Verifying your deposits:\t', show_percent=False, show_pos=True) as deposits: - return all([validate_deposit(deposit) for deposit in deposits]) + return all([validate_deposit(deposit, credential) for deposit, credential in zip(deposits, credentials)]) return False -def validate_deposit(deposit_data_dict: Dict[str, Any]) -> bool: +def validate_deposit(deposit_data_dict: Dict[str, Any], credential: Credential) -> bool: ''' Checks whether a deposit is valid based on the eth2 rules. https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#deposits @@ -45,6 +51,28 @@ def validate_deposit(deposit_data_dict: Dict[str, Any]) -> bool: deposit_message_root = bytes.fromhex(deposit_data_dict['deposit_data_root']) fork_version = bytes.fromhex(deposit_data_dict['fork_version']) + # Verify pubkey + if len(pubkey) != 48: + return False + if pubkey != credential.signing_pk: + return False + + # Verify withdrawal credential + if len(withdrawal_credentials) != 32: + return False + if withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX == credential.withdrawal_prefix: + if withdrawal_credentials[1:] != SHA256(credential.withdrawal_pk)[1:]: + return False + elif withdrawal_credentials[:1] == ETH1_ADDRESS_WITHDRAWAL_PREFIX == credential.withdrawal_prefix: + if withdrawal_credentials[1:12] != b'\x00' * 11: + return False + if credential.eth1_withdrawal_address is None: + return False + if withdrawal_credentials[12:] != credential.eth1_withdrawal_address: + return False + else: + return False + # Verify deposit amount if not MIN_DEPOSIT_AMOUNT < amount <= MAX_DEPOSIT_AMOUNT: return False diff --git a/requirements.txt b/requirements.txt index b47f495a..52bb25b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -36,7 +36,8 @@ pycryptodome==3.9.8 \ --hash=sha256:f2e045224074d5664dc9cbabbf4f4d4d46f1ee90f24780e3a9a668fd096ff17f \ --hash=sha256:f521178e5a991ffd04182ed08f552daca1affcb826aeda0e1945cd989a9d4345 \ --hash=sha256:f78a68c2c820e4731e510a2df3eef0322f24fde1781ced970bf497b6c7d92982 \ - --hash=sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94 + --hash=sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94 \ + --hash=sha256:be7553b8bea117892f83f52ebfe96929340cacae07f3f6a820291e42168dff62 click==7.1.2 \ --hash=sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a \ --hash=sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc @@ -55,17 +56,20 @@ mypy-extensions==0.4.3 \ --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \ --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8 lru-dict==1.1.6 \ - --hash=sha256:365457660e3d05b76f1aba3e0f7fedbfcd6528e97c5115a351ddd0db488354cc + --hash=sha256:365457660e3d05b76f1aba3e0f7fedbfcd6528e97c5115a351ddd0db488354cc \ + --hash=sha256:288d8794e9376fe31d27abc3531d395675b2a910781f26034d5ef79f0ba9cecc pyrsistent==0.16.1 \ --hash=sha256:aa2ae1c2e496f4d6777f869ea5de7166a8ccb9c2e06ebcf6c7ff1b670c98c5ef eth-hash==0.2.0 \ --hash=sha256:1b9cb34dd3cd99c85c2bd6a1420ceae39a2eee8bf080efd264bcda8be3edecc8 \ --hash=sha256:499dc02d098f69856d1a6dd005529c16174157d4fb2a9fe20c41f69e39f8f176 cytoolz==0.10.1 \ - --hash=sha256:82f5bba81d73a5a6b06f2a3553ff9003d865952fcb32e1df192378dd944d8a5c + --hash=sha256:82f5bba81d73a5a6b06f2a3553ff9003d865952fcb32e1df192378dd944d8a5c \ + --hash=sha256:5161bef77f7b69bea64b4e052d0da17845e6adae6d09f91cc7e5acfc5cb2c5c4 toolz==0.10.0 \ --hash=sha256:08fdd5ef7c96480ad11c12d472de21acd32359996f69a5259299b540feba4560 \ - --hash=sha256:e71d8d91c8902fb7659c23e10e9698a8c5cbea985683b8a378c6fd67b52f2fc4 + --hash=sha256:e71d8d91c8902fb7659c23e10e9698a8c5cbea985683b8a378c6fd67b52f2fc4 \ + --hash=sha256:c43f7cffb32a3c8e3fb51192cae4dcdbf0a6fee4d07142ca2e443d2bd9f89400 six==1.15.0 \ --hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259 \ --hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced diff --git a/requirements_test.txt b/requirements_test.txt index e4899000..7e042617 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -78,7 +78,8 @@ typed-ast==1.4.1 \ --hash=sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34 \ --hash=sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe \ --hash=sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4 \ - --hash=sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7 + --hash=sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7 \ + --hash=sha256:3791f73e5d75aa9a95274679ab4821bd9d16de623c4ecf4900a77a29864ee144 packaging==20.4 \ --hash=sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8 \ --hash=sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181 diff --git a/setup.py b/setup.py index e148d351..9976c689 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name="eth2deposit", - version='1.1.1', + version='1.2.0', py_modules=["eth2deposit"], packages=find_packages(exclude=('tests', 'docs')), python_requires=">=3.7,<4", diff --git a/tests/test_cli/test_existing_menmonic.py b/tests/test_cli/test_existing_menmonic.py index 5c3623d1..2ecc97c2 100644 --- a/tests/test_cli/test_existing_menmonic.py +++ b/tests/test_cli/test_existing_menmonic.py @@ -1,16 +1,18 @@ import asyncio +import json import os import pytest - from click.testing import CliRunner +from eth_utils import decode_hex + from eth2deposit.deposit import cli -from eth2deposit.utils.constants import DEFAULT_VALIDATOR_KEYS_FOLDER_NAME +from eth2deposit.utils.constants import DEFAULT_VALIDATOR_KEYS_FOLDER_NAME, ETH1_ADDRESS_WITHDRAWAL_PREFIX from.helpers import clean_key_folder, get_permissions, get_uuid -def test_existing_mnemonic() -> None: +def test_existing_mnemonic_bls_withdrawal() -> None: # Prepare folder my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER') clean_key_folder(my_folder_path) @@ -46,6 +48,57 @@ def test_existing_mnemonic() -> None: clean_key_folder(my_folder_path) +def test_existing_mnemonic_eth1_address_withdrawal() -> None: + # Prepare folder + my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER') + clean_key_folder(my_folder_path) + if not os.path.exists(my_folder_path): + os.mkdir(my_folder_path) + + runner = CliRunner() + inputs = [ + 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', + '2', '2', '5', 'mainnet', 'MyPassword', 'MyPassword', 'yes'] + data = '\n'.join(inputs) + eth1_withdrawal_address = '0x00000000219ab540356cbb839cbe05303d7705fa' + arguments = [ + 'existing-mnemonic', + '--folder', my_folder_path, + '--mnemonic-password', 'TREZOR', + '--eth1_withdrawal_address', eth1_withdrawal_address, + ] + result = runner.invoke(cli, arguments, input=data) + + assert result.exit_code == 0 + + # Check files + validator_keys_folder_path = os.path.join(my_folder_path, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME) + _, _, key_files = next(os.walk(validator_keys_folder_path)) + + deposit_file = [key_file for key_file in key_files if key_file.startswith('deposit_data')][0] + with open(validator_keys_folder_path + '/' + deposit_file, 'r') as f: + deposits_dict = json.load(f) + for deposit in deposits_dict: + withdrawal_credentials = bytes.fromhex(deposit['withdrawal_credentials']) + assert withdrawal_credentials == ( + ETH1_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + decode_hex(eth1_withdrawal_address) + ) + + all_uuid = [ + get_uuid(validator_keys_folder_path + '/' + key_file) + for key_file in key_files + if key_file.startswith('keystore') + ] + assert len(set(all_uuid)) == 5 + + # Verify file permissions + if os.name == 'posix': + for file_name in key_files: + assert get_permissions(validator_keys_folder_path, file_name) == '0o440' + # Clean up + clean_key_folder(my_folder_path) + + @pytest.mark.asyncio async def test_script() -> None: my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER') diff --git a/tests/test_cli/test_new_mnemonic.py b/tests/test_cli/test_new_mnemonic.py index d588db28..4ff1d644 100644 --- a/tests/test_cli/test_new_mnemonic.py +++ b/tests/test_cli/test_new_mnemonic.py @@ -1,16 +1,19 @@ import asyncio +import json import os import pytest - from click.testing import CliRunner + +from eth_utils import decode_hex + from eth2deposit.cli import new_mnemonic from eth2deposit.deposit import cli -from eth2deposit.utils.constants import DEFAULT_VALIDATOR_KEYS_FOLDER_NAME +from eth2deposit.utils.constants import DEFAULT_VALIDATOR_KEYS_FOLDER_NAME, ETH1_ADDRESS_WITHDRAWAL_PREFIX from .helpers import clean_key_folder, get_permissions, get_uuid -def test_new_mnemonic(monkeypatch) -> None: +def test_new_mnemonic_bls_withdrawal(monkeypatch) -> None: # monkeypatch get_mnemonic def mock_get_mnemonic(language, words_path, entropy=None) -> str: return "fakephrase" @@ -49,6 +52,60 @@ def mock_get_mnemonic(language, words_path, entropy=None) -> str: clean_key_folder(my_folder_path) +def test_new_mnemonic_eth1_address_withdrawal(monkeypatch) -> None: + # monkeypatch get_mnemonic + def mock_get_mnemonic(language, words_path, entropy=None) -> str: + return "fakephrase" + + monkeypatch.setattr(new_mnemonic, "get_mnemonic", mock_get_mnemonic) + + # Prepare folder + my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER') + clean_key_folder(my_folder_path) + if not os.path.exists(my_folder_path): + os.mkdir(my_folder_path) + + runner = CliRunner() + inputs = ['english', '1', 'mainnet', 'MyPassword', 'MyPassword', 'fakephrase'] + data = '\n'.join(inputs) + eth1_withdrawal_address = '0x00000000219ab540356cbb839cbe05303d7705fa' + arguments = [ + 'new-mnemonic', + '--folder', my_folder_path, + '--eth1_withdrawal_address', eth1_withdrawal_address, + ] + result = runner.invoke(cli, arguments, input=data) + assert result.exit_code == 0 + + # Check files + validator_keys_folder_path = os.path.join(my_folder_path, DEFAULT_VALIDATOR_KEYS_FOLDER_NAME) + _, _, key_files = next(os.walk(validator_keys_folder_path)) + + deposit_file = [key_file for key_file in key_files if key_file.startswith('deposit_data')][0] + with open(validator_keys_folder_path + '/' + deposit_file, 'r') as f: + deposits_dict = json.load(f) + for deposit in deposits_dict: + withdrawal_credentials = bytes.fromhex(deposit['withdrawal_credentials']) + assert withdrawal_credentials == ( + ETH1_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + decode_hex(eth1_withdrawal_address) + ) + + all_uuid = [ + get_uuid(validator_keys_folder_path + '/' + key_file) + for key_file in key_files + if key_file.startswith('keystore') + ] + assert len(set(all_uuid)) == 1 + + # Verify file permissions + if os.name == 'posix': + for file_name in key_files: + assert get_permissions(validator_keys_folder_path, file_name) == '0o440' + + # Clean up + clean_key_folder(my_folder_path) + + @pytest.mark.asyncio async def test_script() -> None: my_folder_path = os.path.join(os.getcwd(), 'TESTING_TEMP_FOLDER') diff --git a/tests/test_credentials.py b/tests/test_credentials.py index fa14b6be..53669301 100644 --- a/tests/test_credentials.py +++ b/tests/test_credentials.py @@ -13,4 +13,5 @@ def test_from_mnemonic() -> None: amounts=[32, 32], chain_setting=MainnetSetting, start_index=1, + hex_eth1_withdrawal_address=None, ) diff --git a/tests/test_key_handling/test_key_derivation/test_vectors/mnemonic.json b/tests/test_key_handling/test_key_derivation/test_vectors/mnemonic.json index 843afd21..11b7a6ea 100644 --- a/tests/test_key_handling/test_key_derivation/test_vectors/mnemonic.json +++ b/tests/test_key_handling/test_key_derivation/test_vectors/mnemonic.json @@ -729,6 +729,152 @@ "xprv9s21ZrQH143K2JNoRzEr4Hk99mxRnwy8tdhjFuDfQ1mxLiu4BQSwTuVdZbPgu6ykRXbR7MWhkH6mQACGV3DVChebP3iFp8RvY56hNAcN6yf" ] ], + "portuguese": [ + [ + "00000000000000000000000000000000", + "abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abater", + "ab9742b024a1e8bd241b76f8b3a157e9d442da60277bc8f36b8b23afe163de79414fb49fd1a8dd26f4ea7f0dc965c760b3b80727557bdca61e1f0b0f069952f2", + "xprv9s21ZrQH143K4ayH97er9xirrbGL9hEywmMfh1LdQjwtrsnrvENN7c2yKs82HypXo4GAMt4wpnna9doa1FDFYXQTSFReWDY4XAf4imN5m6s" + ], + [ + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "imitador vinheta sogro xerife veleiro pomar volumoso tratador imitador vinheta sogro xingar", + "298d1614ff06ae803709f5be5331135cb74e6cc77fa09e07a3e887c2e370401f9a73a409dadf58b5a5197b27ffb3fa5dd528aad9a1a8750d7669ce950ee60c2c", + "xprv9s21ZrQH143K3H4uYufypQuRqf28rLoZpLTwjpeCiPBpcZtJpupYd2oS39oGnrHd7fPAgU5d8bfTpYd97MShp4f9fBGj5K1F9NqgZFpmbVj" + ], + [ + "80808080808080808080808080808080", + "inalador acirrar barulho abotoar afivelar coruja abutre amostra inalador acirrar barulho abduzir", + "800fd4e7691fbc3ceed246c211a38949c3607fe269a35829e40ca9d3e26515a4ebd64d8bfe9b66b49543fe9dab78bde7cb7102968ce669f55293bcc02e26ba0e", + "xprv9s21ZrQH143K2qEuVaHnXDY1HbcQ9nMyNoi5tgGyFxfYXVxHipQ9hcmS2QitJxsNFatCZk4EYoyyNdWamAjJiMLhuimpjiWHPvJCw28EMSr" + ], + [ + "ffffffffffffffffffffffffffffffff", + "zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido xeque", + "7fb404372815ea28ef97a64249acd71a293ea0437b3dac8f7e193a10f3584e2055753cc8d6f025229f65e61318fc4e10d4017bd3cc3496f535eca3247d26acd6", + "xprv9s21ZrQH143K3tfxkHq74PSrq4HqTjs5sHHxBpjoP26GnJ5Q8w7tEn5kmPESuqN5TQb9Lu4LysqMAcXHWNsr3DKSnvuu3vx6sK7V6Hhv2iD" + ], + [ + "000000000000000000000000000000000000000000000000", + "abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate acumular", + "81c66b6789e8b91c169335be4436fd9736ca9c06425acd09b0525e1d6836383130f7f7d31378aaef8b7109503972f40d42f6c6b9f99765827bea762515d3404d", + "xprv9s21ZrQH143K4LsdmZ3NinpZHGbQ6n7aQr5uV8Rv7Fq8yNPdAzsyrezon8NZ3ZCzode5jS2PXzN1C68amwDmp2bgAEF4jDG7NUbdRYCjR8r" + ], + [ + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "imitador vinheta sogro xerife veleiro pomar volumoso tratador imitador vinheta sogro xerife veleiro pomar volumoso tratador imitador viga", + "17040704dd985478b7d0666c7078201e3cd7d1fd1aca0d7d47c98a91ec7845500c611d987339a1d4c12bc506feb7c486eef0aa8ce679b1d184db5ca40fe8ef67", + "xprv9s21ZrQH143K3XAGBJxF36qFzxkFaSEsNjqKvCtT3s7WfzgMBRcv8G9K2CRGXXhLYPWKSipBrd9dyLSRuTzQWbAJkhghki7Nn13QfHqCSBq" + ], + [ + "808080808080808080808080808080808080808080808080", + "inalador acirrar barulho abotoar afivelar coruja abutre amostra inalador acirrar barulho abotoar afivelar coruja abutre amostra inalador afastar", + "0f637bf3a487c26fb73f6a464f62ef1f6ca73a6ae083e220374c82881bf4ed2dafd874956ce368c4441e6269759c5864197e87421fbcdb7f6d63df17b4f7df81", + "xprv9s21ZrQH143K25VJsZdwtDCmzZ5C2LkJyNDKrHiFfHHBEmtvn77g7qrBRGHTum98TqU7sWCGFZ8vjtcPVAfgGcV51vi2BrPR66KC7wNxb3z" + ], + [ + "ffffffffffffffffffffffffffffffffffffffffffffffff", + "zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido viaduto", + "042e40dacea1df76335445c37171e5c0fde334236abe1b5d69378548875d157968dfff5889641f16690dca9baa4d9e5fbf56e3aaf0765144ba96b819f37fd0ae", + "xprv9s21ZrQH143K3xAXH3i9VReyK3HSyzSmN11DXikYFTA3senE3iWDCdYTFXL9JkBM7CeCS31NBBHNu2tH5T1iDWCBEu1TeWLfELH4CaGAiRR" + ], + [ + "0000000000000000000000000000000000000000000000000000000000000000", + "abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate abacate alinhar", + "8fe67c9f53a30f75513830e18f6bd0950354297a4977393fae3577363393e679cc13452bcfc9460b28a913ab8de9efc55f5901d1ba77e5eec791afd967768607", + "xprv9s21ZrQH143K3UVmtSkznKJCHUGjkjoHkUd6dxdfo91K8sdpEVmzjyRMB3Sf2WC7WKzmZLw2cfJ43LAEENq2v1gASK4TmMUTkbWVoJaZLge" + ], + [ + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "imitador vinheta sogro xerife veleiro pomar volumoso tratador imitador vinheta sogro xerife veleiro pomar volumoso tratador imitador vinheta sogro xerife veleiro pomar volumoso sucata", + "feac9ad4e1a4a4399a7d57fe47bf64b404a7588eca1025abfa299365f7a75639317e2c89a94812db33405aa0213846bfd6d53dfd02743e2cf3b6984eb9fcf19f", + "xprv9s21ZrQH143K2Syz6ZuTcKmPtPee4Xn5HMEnuZcNZ1s7hJdgjRLFJ8f4d8DnoBBTUchCBjBnjubsz3wum7zbhdp5h9hm4g6DVDo2LaHGJ2a" + ], + [ + "8080808080808080808080808080808080808080808080808080808080808080", + "inalador acirrar barulho abotoar afivelar coruja abutre amostra inalador acirrar barulho abotoar afivelar coruja abutre amostra inalador acirrar barulho abotoar afivelar coruja abutre asilado", + "32c8feae6a0bee33166468a770cb28459727e10f4f5ffef64977d5ef52a68ec51d832751a10c025058612ab256052cdfa9d8c5c87560de0453efe5a7d4597771", + "xprv9s21ZrQH143K4XMzpdgrkDGxnU31ezynvtw3iBEfRB8sriHpEwyu2Gb65ao9YLcTTWTyxNh2g2AdtYmZ4wpTaAxLpJBzWY7jAvG1HyotoEt" + ], + [ + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido zumbido validade", + "739a6edd208d09e28fa97f8e8709aaf185e173125b1b427c04d1539173c88b78e81610a759e14f97a2038dcdc2c466a072788e3d7c88cc9bf36b96cb29510e77", + "xprv9s21ZrQH143K45qUnDL5tCHKGpht6amXS4d278iTXWRJXthNTufwnbnvr6rU8kd4Nb5s9aBWdYFtfcdGkg4GdL91jRhGRtP8mZbjtTvpGsy" + ], + [ + "2100b925962774a62a89e1218c945fc9", + "batucar aderente depois cacique granada educado nudez colidir beirada fantoche assador listagem", + "d5215d82f740138009026200b21017a520c1b814451a5829511d836dcb65dd82b7a75cc89d1371893beee8f7c053f08452eb72357ee5f3838ec04f4dad1b958c", + "xprv9s21ZrQH143K29C5RjXaAPKebhowps7kPSRatNpAv7pGPaWVzyosMCdR4uwUyjaMT572NDsycVM4aFb7ij4f1MAp8ukEXmonK6g8Swmb6rS" + ], + [ + "a8a8b4db0731bda3f9f6a9f8543dc34df124b0ca7839e99c", + "noiva datar cheque amador avaria revisor tarja rotular vencedor molusco garagem megafone anuidade enraizar bronzear adverso telhado tampa", + "15bf50d63afb7b0fd62340821d402c835eedab990d94178b4a469982618226aa74704eeaf43e71dded32054a58ffab278fa63d831c6851b40e2834cdf6018b0c", + "xprv9s21ZrQH143K4KpekioHt47zJYZrK8UNu4ToQD7SF9oeVEQvov7SC4SoyK54NAMnFrKjmZSKeD21B5XRzXGy2HiLqzCoWnVAG1dCDbR1vvv" + ], + [ + "32c396807d75efde8fef5edd76256cdc526739802feed79cdd1cfbd615926b25", + "casulo azarar miolo vidraria esvaziar trajeto corneta timidez setembro ostentar ereto pendurar bobagem menos abelha voltagem saibro tampa deduzir vegetar executar rapadura ofensiva nupcial", + "ac8c7484fcd3e4f813bacc335eb88818198c20a0f8301a444cf0bb4370607afbe620ca9b0e330821b0b54eacac73b2662146a2475964f5fc7ec112b1cb32e6ff", + "xprv9s21ZrQH143K3sSwypiuPXXTXqn62DTUj2PWzcD9Ukzj1Lw4549PUVhN6jTwaYZvd1KMayrz7snigDppSNZ6NWm7YUEfjdna4cTZGBug3hr" + ], + [ + "0d20c5e9c396b2b0aaed9ad4067faa0d", + "alocar adiante habitar italiano fogo enquanto obturar pagode roleta cedilha tese atriz", + "dae012905762f10456527b3305e6b5017a3baaac7766eb18f34862a4c7df6233b2d21047db64adfbcd76be405c3dc35e00a87eaddef6d5ad9ba3283bc2841d65", + "xprv9s21ZrQH143K4FfZqJQqwEDXKA6z13eT68UQpK7Sfbt2zSY1j88mYm3z5DbRXhxXW8ur4zYGPzXEkpm8a8D3FsJmkSxbriXkvAZX7AtFjXZ" + ], + [ + "f7dc48c266b26679791cdff97e378418be82895f298f521f", + "veicular suavizar capotar refogar bobagem comando suplicar megafone verniz triunfal sono careca taurino curativo homem carreira duquesa volumoso", + "f1fcddae2074db206e38b69701e4bee723cdb68d98e07a5d7f516a0380b5002a8eb4dfb267ae1190f47ab69e0344bc167a87b0df9eab8b1f945b55003d2b3c1d", + "xprv9s21ZrQH143K3fQN4sbpdW2WeJkJrXCfhM2SHEVViPjzPF4DLkN13BVm5MZVQ7DVZrk23mBZwYabaqABqi7m6Q4qZL7NgJHz1BEJpHcR9eq" + ], + [ + "80d3b3b0ad1f4bc82bf5b29cf400daf69c26823e732d7b230e66199dab18eaa8", + "inativo mesclar tiragem enviado urubu sumir orgasmo passado mensagem miolo cheiro tonel presilha couve vespa fator heresia astral marinho caule seda lapidar nuvem barato", + "727058ef87d1395f7fbacd404591069e0d5f5aa173d59734562edc7139491c95f33d51e1cee96d6fc21da9bfde4b2f7230d291674a5ff1fc443ac88880f7f397", + "xprv9s21ZrQH143K37y1ffH4JpPwHYbXMN7DSivUH2bd6FmoPns86CjY6FVmkVU8PUzu2ssAveptXKAmBtAFhvWHTVvcUnShvjkGWNMNKq7cSFp" + ], + [ + "611ee6424f30bd95a6463003bcd43eea", + "excitar vazar levitar mexer alertar ratoeira marcador quantia acidez tamanho amolador romano", + "075a48780e7bae6891db76ff1cb64965b3153a120fa075d28b8cb47299741b30de2a1c5e0d29317f31e90d52e1f64666c0739982feb288c6f1ac4486062f3838", + "xprv9s21ZrQH143K3cY2TBr4GvNLgfchHzsgmpTF7MSgxmKsryjQgbaHQ8jNnaJBFBofAfSinn8KncGPNPqaKMaffHvjBpYhrxRSMnKstbKm1vD" + ], + [ + "ce749ee96f80a3a1209aca95951d8f7ea030985caa4ba22c", + "rejeitar museu pertence sirene ajoelhar resolver infinito entanto macete nominal fadiga voar acenar deusa gemada depois moqueca fechado", + "beae4e69f987cc1a7193371d13e195573607cbc307066875a385cddd9aaefeb2014dbfaff01f69a0c323252169db55ac38db7dd9e842124377e587d468c34dbe", + "xprv9s21ZrQH143K3A9B5rU1jjqwQMrdewYNTWni4ayiXKTo1UjVKQmdQKHJzkMiHosT9TL3F1qE6GspUMZTqYARxHJg39tgRZUAfahhdYrSorM" + ], + [ + "fcd16e49dd2cce32828a989eb4fef35b9c5c045b66bdf0380d1255f20c236e33", + "vitral lagarta litoral pertence reduzida aterro ajoelhar educado micro ninar pipoca pausar puxador acabado frieza salada treino possuir cupido ocioso batida anjo peneira gracejo", + "e3b378f417b88f6222c774b96f51fdb3f82678b18ecd79e100417f8913936cdf6b21f924c77db84e6cc4fcb4a8cd858927c231e9d4af6e18db8a5992e82f2b87", + "xprv9s21ZrQH143K2kiM2aF9ZV5EMWGMMrK16GTgkpNCxZcK73ew9nPzrCErLo1DyK7hggAUu2g2DTpnyPRdbkjdxK88Db8b3BgBiYzhc9vxFbv" + ], + [ + "e1db065d58677944f60def8b25f02752", + "sotaque salpicar mamoeiro oscilar graveto morcego salpicar planta lactante canivete agrupar muralha", + "feaa37f688901a57c6b85d70dbe531594ebe78341ce9729aee89f5e2da454245fc98afcf815356a356a1f6f28e7135795e6e44d4be39cdd438ab1f20fa017ae4", + "xprv9s21ZrQH143K25UrPqy18MGFv8WSceqMKkgSZefvonGM6AegMu1jYhfG7h5j1xkFQzchrrnnVD6zUf5cXQv9vV4sEzzXHYoS9R3Mm2d6hVF" + ], + [ + "9b3ea73454d3d40c6f9781da53546006716b410e95f9db56", + "matinal vacina reenvio novelo complexo adiante plebeu treino sazonal massagem assinar aliviar arriscar mocidade coagir polegada segmento papagaio", + "084098553ddb458305e2f57c62bc146a3d6803e40ca2a11c91ad03c444646188837289c7fce8ce1d089886d3829b85f4ecf076a106b74b06248e926eaa291829", + "xprv9s21ZrQH143K3M5Njh7UvZk2VBUdHW2zGAXzoyUTZr1rniQhmuvT3tNrCKbEdSAz3hX3zuFGQrdGraQKgLakACR75ZvrjUiJHgPWB6jj8An" + ], + [ + "71566846d21eaef06230506ab4f4e29bd0dafc2869f2404ccbc6b3ecdd31a032", + "garoupa pagode anjo multar teto grilo joelhada agulha fluxo nevasca cimento chutar alucinar iguana molho conjugar coruja feijoada triunfal feudal regalia mapeado inativo etiqueta", + "7c0291cfa7619c7882462c214aa8c821ed66f7bebe9ec8a8c22e0c9a746d8831fe73c9019b32ffd023899f73a83b8cf387cf460411e0bdd82f3f5b6e4114bc35", + "xprv9s21ZrQH143K4MMeaU4Lk7KnY4G79oLbqAmvatuXdS9VYohix457YS93fouYRP9jxPAXdAzqiaEmTG8aTcY8YbeCn8wbAMABznnMmiKGdVN" + ] + ], "spanish": [ [ "00000000000000000000000000000000", @@ -875,4 +1021,4 @@ "xprv9s21ZrQH143K2wGULuPcZL2f64mqVeYm4jfXrUoTYm1FE2Xv9RBkvrqhT1oCoGz3v8bcPvUwiUACptNoPkXHnCPcTcpdopokhJhLrYvgA1w" ] ] -} \ No newline at end of file +}