Step-by-Step Process:
const { Keypair } = require('stellar-sdk');
const issuingKeypair = Keypair.random();
console.log('Public Key:', issuingKeypair.publicKey());
console.log('Secret Key:', issuingKeypair.secret());
const fetch = require('node-fetch');
async function fundAccount(publicKey) {
const response = await fetch(`https://friendbot.stellar.org?addr=${publicKey}`);
const responseJSON = await response.json();
console.log('Friendbot response:', responseJSON);
}fundAccount(issuingKeypair.publicKey());
const { Server, TransactionBuilder, Operation, Asset, Networks } = require('stellar-sdk');
const server = new Server('https://horizon-testnet.stellar.org');
const bookAsset = new Asset('BookToken', issuingKeypair.publicKey());
async function createBookAsset() {
const account = await server.loadAccount(issuingKeypair.publicKey());
const transaction = new TransactionBuilder(account, {
fee: await server.fetchBaseFee(),
networkPassphrase: Networks.TESTNET,
}).addOperation(Operation.changeTrust({
asset: bookAsset,
source: issuingKeypair.publicKey(),
})).addOperation(Operation.payment({
destination: issuingKeypair.publicKey(),
asset: bookAsset,
amount: '0.0000001',
})).addOperation(Operation.manageData({
name: 'isCheckedOut',
value: 'false',
source: issuingKeypair.publicKey(),
})).addOperation(Operation.manageData({
name: 'checkedOutBy',
value: '',
source: issuingKeypair.publicKey(),
})).setTimeout(100)
.build();
.sign(issuingKeypair);
transactionawait server.submitTransaction(transaction);
console.log('Book asset created with isCheckedOut and checkedOutBy properties set');
}
createBookAsset();
a. Initial Request:
b. Verifying Availability:
isCheckedOut
status of the book
asset on the Stellar blockchain.
javascript async function checkBookAvailability(bookAsset) { const account = await server.loadAccount(issuingKeypair.publicKey()); const isCheckedOut = account.data_attr['isCheckedOut']; return isCheckedOut === 'false'; }
c. Creating Token or Key for Encrypting Checked Out Book:
If the book is available, the system generates a unique token using a Soroban smart contract. ```rust pub fn check_out_book(env: Env, book_id: Symbol, user: Address, return_date: Timestamp) -> BytesN<32> { let is_checked_out = env.storage().has(&book_id);
if is_checked_out {
let checked_out_status: bool = env.storage().get(&book_id).unwrap().unwrap();
if checked_out_status {
panic!("Book is already checked out");
}
}
env.storage().set(&book_id, &true);
let checked_out_by_key = Symbol::from_str("checkedOutBy");
env.storage().set(&checked_out_by_key, &user);
let return_date_key = Symbol::from_str("returnDate");
env.storage().set(&return_date_key, &return_date);
let new_token = env.random().generate();
let token_key = Symbol::from_str("checkoutToken");
env.storage().set(&token_key, &new_token);
new_token
} ```
d. Updating Blockchain with New Info (User Address, Checkout Status):
isCheckedOut
status to
true
, stores the user’s address, and the return date.
rust env.storage().set(&book_id, &true); env.storage().set(&checked_out_by_key, &user); env.storage().set(&return_date_key, &return_date);
e. Delivery of Encrypted eBook File:
The system encrypts the eBook using the generated token and delivers the encrypted eBook to the user. ```javascript const crypto = require(‘crypto’);
function encryptEBook(eBookContent, token) { const algorithm = ‘aes-256-ctr’; const cipher = crypto.createCipheriv(algorithm, token, Buffer.alloc(16, 0)); const encrypted = Buffer.concat([cipher.update(eBookContent), cipher.final()]); return encrypted; }
const eBookContent = Buffer.from(‘This is the content of the eBook’); const token = /* token from the smart contract */; const encryptedEBook = encryptEBook(eBookContent, token); // Deliver the encrypted eBook to the user ```
a. Update Asset Availability on Blockchain:
When the user returns the book or the due date passes, the smart
contract updates the isCheckedOut
status to
false
. ```rust pub fn return_book(env: Env, book_id:
Symbol, user: Address) { let is_checked_out =
env.storage().has(&book_id);
if !is_checked_out {
panic!("Book does not exist");
}
let checked_out_status: bool = env.storage().get(&book_id).unwrap().unwrap();
if !checked_out_status {
panic!("Book is already returned");
}
let checked_out_by_key = Symbol::from_str("checkedOutBy");
let stored_user: Address = env.storage().get(&checked_out_by_key).unwrap().unwrap();
if stored_user != user {
panic!("Unauthorized return");
}
env.storage().set(&book_id, &false);
let token_key = Symbol::from_str("checkoutToken");
env.storage().remove(&token_key);
} ```
b. Expire Key or Invalidate Token:
The smart contract invalidates the token, making the eBook unreadable to the previous borrower. ```rust pub fn invalidate_token(env: Env, book_id: Symbol) { let return_date_key = Symbol::from_str(“returnDate”); let stored_return_date: Timestamp = env.storage().get(&return_date_key).unwrap().unwrap();
if env.now() > stored_return_date {
env.storage().set(&book_id, &false);
let token_key = Symbol::from_str("checkoutToken");
env.storage().remove(&token_key);
}
} ```
This detailed walkthrough outlines the process of adding an eBook to the Stellar blockchain as an asset and the steps involved in the checkout and return processes using Soroban smart contracts. By leveraging blockchain technology, we can ensure that eBooks are securely and efficiently managed, maintaining the principles of the first-sale doctrine in the digital age.