Certificación de Títulos
Ingrese el Nº de Serie de su tìtulo para confirmar su certificación en Blockchain.

Títulos validados

1104151


Transacciones validadas

1525

Título Certificado

Certificado MINED

Ministerio de Educación de la República Argentina

Certificado MINED Títulos digitales - está vigente

Hash Blockchain Ministerio de Educacion de la Nacion:
N° de bloque: 37653132
ID de bloque: 0x613c37d4ebb4d5ca0a2ef1518ee1e854f680981fa3cfe7b3e7e65e1a8e09e06c


Nº de Serie:

14-00011879-2024

Hash del Contenido Analítico:

0b5df2567eb461656ab22650a0469aec64c2fc3d67bcffd827605fe6cf0fb56091d02da90ebcc7d8e243265442a1880943e0d6e1a437fe53a416c7119908d06c

Fecha y hora de emisión del título:

hace 8 meses (03/10/2024 12:00:23)

Hash de transacción:

0x3b2a3c1efaa3b0db82238e071a993848dacfd98f02c7d92ddba6caaefbc20b0b

Cabeza de Merkle:

0x1d0bb6bb2126a3502b3fa02b76dda83d26a0378d467926a1bf4c820ca252f8e8

Dirección del contrato:

0x7e56220069CAaF8367EA42817EA9210296AeC7c6

Status:

Success

Arbol de Merkle del Sistema Nacional de Titulos Digitales

El árbol de Merkle es una estructura de datos utilizada en criptografía y cadenas de bloques para verificar rápidamente la integridad de grandes conjuntos de datos. Se construye mediante funciones hash, donde cada bloque de datos se asigna a un nodo hoja, y los nodos se combinan hasta formar una "raíz de Merkle". Esta raíz se utiliza para representar de manera única todos los datos, y cualquier cambio en los datos afecta la raíz, facilitando la detección de manipulaciones en sistemas distribuidos como la cadena de bloques.

Datos Iniciales: Cada bloque de datos (por ejemplo, transacciones en una cadena de bloques) se asigna a un nodo hoja del árbol.
Para éste caso particular, nuestros datos iniciales serán la concatenación del N° de Serie y el Hash del Contenido Analítico

Hash de Hojas: Se aplica una función de hashing a cada bloque de datos inicial para crear los nodos hoja del árbol.
La función de hashing que utilizamos se llama keccak-256, la misma utilizada por la Blockchain Ethereum para generar sus hashes de transacción, direcciones, etc.

Combinación de Nodos: Los nodos hoja se emparejan, concatenando sus hashes y aplicando a esta concatenación la misma función de hashing. El valor resultante se almacena en un nodo "padre" de sus dos "hijos". Este proceso continúa hasta que solo queda un único hash, conocido como la "raíz de Merkle".

Raíz de Merkle: La raíz de Merkle se utiliza para representar de manera única todos los datos incluidos en el árbol. Cualquier cambio en los datos originales alteraría la raíz de Merkle.

Validación: Para validar que un valor en particular fue utilizado para construir una raíz de Merkle, no es necesario reconstruir el árbol entero con todos los datos iniciales.
La cantidad de nodos mínimo necesarios para realizar la validación es igual al logaritmo en base 2 de la cantidad de datos iniciales.
Los nodos en particular necesarios son el nodo hermano del nodo con el valor en cuestión, el hermano del nodo padre, el hermano del nodo abuelo, etc.


A continuación, una representación visual de la verificación del uso de los datos de éste título en la generación de la Raíz de Merkle metida en la Blockchain.
Haciendo click en cada nodo del tronco central, verá como fue construído.

Datos del contrato

Nombre del Contrato: SelladorFederaldeTitulos
Versión de Compilador: v0.5.2

Código Fuente del Contrato (Solidity)
SelladorFederalTitulos.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.5.2;

import "./Ownable.sol";

contract SelladorFederalTitulos is Ownable {
    address private _owner;

    struct Item {
        uint256 timestamp;
        uint256 blockNumber;
    }

    event ItemAdded(
        bytes32 indexed itemId,
        uint256 timestamp,
        uint256 blockNumber
    );

    mapping(bytes32 => Item) public items;

    constructor() public Ownable() {
    }

    function put(bytes32 key) public onlyOwner {
        if (items[key].timestamp == 0) {
            items[key] = Item(block.timestamp, block.number);
            emit ItemAdded(key, block.timestamp, block.number);
        }
    }

    function exists(bytes32 key) public view returns (bool) {
        return items[key].timestamp > 0;
    }

    function get(bytes32 key) public view returns (uint256 timestamp, uint256 blockNumber) {
        Item storage item = items[key];
        return (item.timestamp, item.blockNumber);
    }

    function verify(bytes32 leafNode, bytes32[] memory proof) public view returns (bool) {
        bytes32 currentHash = leafNode;

        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];

            if (currentHash < proofElement) {
                currentHash = _hash(abi.encodePacked(currentHash, proofElement));
            } else {
                currentHash = _hash(abi.encodePacked(proofElement, currentHash));
            }
        }

        return exists(currentHash);
    }

    function _hash(bytes memory packedData) private pure returns (bytes32) {
        return keccak256(packedData);
    }

}

                
Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.5.2;

contract Ownable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() public {
        _transferOwnership(msg.sender);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view {
        require(owner() == msg.sender, "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

                
Contract ABI
[
    {
        "constant": true,
        "inputs": [
            {
                "name": "",
                "type": "bytes32"
            }
        ],
        "name": "items",
        "outputs": [
            {
                "name": "timestamp",
                "type": "uint256"
            },
            {
                "name": "blockNumber",
                "type": "uint256"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    },
    {
        "constant": false,
        "inputs": [],
        "name": "renounceOwnership",
        "outputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "owner",
        "outputs": [
            {
                "name": "",
                "type": "address"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    },
    {
        "constant": false,
        "inputs": [
            {
                "name": "newOwner",
                "type": "address"
            }
        ],
        "name": "transferOwnership",
        "outputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "constructor"
    },
    {
        "anonymous": false,
        "inputs": [
            {
                "indexed": true,
                "name": "itemId",
                "type": "bytes32"
            },
            {
                "indexed": false,
                "name": "timestamp",
                "type": "uint256"
            },
            {
                "indexed": false,
                "name": "blockNumber",
                "type": "uint256"
            }
        ],
        "name": "ItemAdded",
        "type": "event"
    },
    {
        "anonymous": false,
        "inputs": [
            {
                "indexed": true,
                "name": "previousOwner",
                "type": "address"
            },
            {
                "indexed": true,
                "name": "newOwner",
                "type": "address"
            }
        ],
        "name": "OwnershipTransferred",
        "type": "event"
    },
    {
        "constant": false,
        "inputs": [
            {
                "name": "key",
                "type": "bytes32"
            }
        ],
        "name": "put",
        "outputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [
            {
                "name": "key",
                "type": "bytes32"
            }
        ],
        "name": "exists",
        "outputs": [
            {
                "name": "",
                "type": "bool"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [
            {
                "name": "key",
                "type": "bytes32"
            }
        ],
        "name": "get",
        "outputs": [
            {
                "name": "timestamp",
                "type": "uint256"
            },
            {
                "name": "blockNumber",
                "type": "uint256"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [
            {
                "name": "leafNode",
                "type": "bytes32"
            },
            {
                "name": "proof",
                "type": "bytes32[]"
            }
        ],
        "name": "verify",
        "outputs": [
            {
                "name": "",
                "type": "bool"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    }
]