¿Cómo evitar usar tx.origin cuando realmente lo necesitamos?

Se ha recomendado que evitemos usar tx.origin.

Sin embargo, hay algunos casos en los que es realmente necesario, como el siguiente caso.


Supongamos que tenemos dos contratos inteligentes: Sc1 y Sc2, ambos creados por el mismo propietario . Primero despliega Sc1 que mantiene un saldo de fichas. Luego, el propietario crea otro contrato inteligente Sc2, lo implementa y permite a los usuarios interactuar con Sc2 y transferir algunos de sus tokens de Sc1 a Sc2 (suponga que algunos usuarios tienen tokens en Sc1). Pero solo los propietarios de tokens deberían poder hacer eso, nadie más (ni siquiera el propietario de los contratos ).

Como puede ver a continuación, tengo que usar tx.origin. Soy consciente de que si el propietario del contrato crea otro contrato no válido y hace que el usuario (propietario del token) lo llame, podrá disminuir su token_bsalance en Sc1. A continuación se muestra el código para el caso anterior (el ataque está excluido).

   contract Sc1{
      mapping (address => uint) token_balance;
      mapping (address => bool) valid_caller_contracts;// keeps track 
                                                      //of those contract that can call this contract
      address owner;
      function Sc1(){
        owner = msg.sender;
      }
       // only owner must be able to register. 
      function register_valid_contract() external {
          require(tx.origin==owner);
          valid_caller_contracts[msg.sender] = true;
      }
      function decrement_token(uint val) external{
        require(token_balance[tx.origin] >= val);
        token_balance[tx.origin] -=val;
      }
   }

   contract Sc2{
     mapping (address => uint) token_balance2;
     address owner;
     function SC2(){
        owner = msg.sender;
      }
      function register() external{
       Sc1 c1=Sc1(0x22);//Let's assum the address of Sc1 after it's 
                        //deployed is 0x22
       c1.register_valid_contract();
      }
      // only those who have tokens in Sc1 must be able to tranfer 
      //tokens to Sc2.
      function fetch_tokens(uint val) external{
       Sc1 c1=Sc1(0x22);//Let's assum the address of Sc1 after it's 
                        //deployed is 0x22
      c1.decrement_token(val);
      token_balance2[msg.sender] += val;
      }
    }

Pregunta : En el caso anterior, ¿podemos evitar el uso de tx.origin?

Respuestas (1)

Un problema grave para un contrato en función tx.origines que su contrato no funcionará correctamente con billeteras multisig (o cualquier otro contrato inteligente).

Una solución para el problema register_valid_contractes pasar la dirección del contrato como parámetro y el propietario tiene que llamar a la función como un paso adicional.

function register_valid_contract(address sc2) external {
    require(msg.sender == owner);
    valid_caller_contracts[sc2] = true;
}

Otro enfoque es que SC1 usa patrones de fábrica para crear SC2 y los registrará inmediatamente en la creación.


Puede decrement_tokenpasar msg.sender como parámetro, pero debe asegurarse de que el remitente sea un contrato válido.

function decrement_token(address from, uint val) external{
    require(valid_caller_contracts[msg.sender]);
    require(token_balance[from] >= val);
    token_balance[from] -=val;
}

En este ejemplo, si define la relación de confianza entre SC1 y SC2, por ejemplo, si SC1 crea SC2, entonces SC1 puede verificar que la dirección del remitente sea SC2 y confiar en que los parámetros serán correctos.

Muchas gracias por la respuesta. Solo quería mencionar que en su solución propuesta (aunque es correcta) las partes deben interactuar directamente con SC1, mientras me preguntaba si pueden hacerlo a través de SC2. ¡Gracias de nuevo!
El problema es que debe llamar a SC1 desde SC2, y SC1 no sabe nada sobre SC2 (puede ser un atacante). Una solución es que el propietario envíe una firma con el mensaje, SC1 puede recuperar la dirección con ECRECOVER y validar que es del propietario. Otra solución es hacer que un tercer contrato cree SC1 y SC2 y los registre mutuamente en la creación.
Gracias por tu comentario. Con respecto a la primera solución (si quiere decir que el remitente envía un mensaje y su firma a SC2, entonces tanto SC1 como SC2 verifican la firma), ¿no cree que habrá un ataque de repetición? Porque se puede copiar el mensaje y la firma del remitente y enviarlo desde otro contrato al SC1. En este caso, el SC1 siempre lo valida.
Puede incluir más información en la firma, la dirección de SC2, una marca de tiempo, para que SC1 pueda verificar que SC2 es la persona que llama y que la fecha de firma está cerca de la transacción.