How To Find the Locks on Bitcoin.

Zachary Weiner
6 min readNov 11, 2023

--

Introduction To Locking

Locking Bitcoins with nTimeLock is something that's been available forever. But now, thanks to Jack Liu theres now a new kind of locking that creates a mashup of the safety from the HODL mentality, while still adding compounding transaction volume to the network. These LooLocks are more meaningful to the network than simply putting a lock on your coins to make sure they cant be spent. LooLocks locks allow us to temporarily lock a portion of our Bitcoin as a means of signaling the importance or relevance of certain content on the network.

You use your coins, add to the Bitcoin brain, and inject transaction velocity … but in the end you get your coins back from the lock-box.

In this article unique indexing script that monitors these locks, offering a fascinating glimpse into what the community finds valuable.

What are Bitcoin Locks?

Before diving into the script, let’s briefly explain Bitcoin locks. Essentially, they enable users to “lock up” a fraction of Bitcoin for a set period in one transaction output, while creating key-value pairs in another output following the using Bitcoin schema (https://bitcoinschema.org/#/)

One key pair must includes the transaction ID of the content against which the Bitcoin is locked. This is seen as a social signal within the Bitcoin network, highlighting content deemed noteworthy by users.

The Indexing Script: An Overview

The script is designed to listen to JungleBus, a service that provides real-time updates on Bitcoin transactions. It listens for new locks and, upon identifying one, checks if the amount locked exceeds a predefined threshold. If it does, the script delves deeper to uncover the content associated with the lock and retrieves the original poster’s handle.

Key Features of the Script:

  • Real-time Monitoring: Continuously listens for new lock transactions on JungleBus.
  • Transaction Analysis: Examines the outputs of each lock transaction to determine its significance.
  • Content Retrieval: Fetches the content linked to significant locks and identifies the content creator’s handle.
  • Data Caching: Uses an in-memory cache to store responses for efficiency.
  • State Tracking: Appends transaction states to a file for record-keeping.

Technical Breakdown

The script, written in Node.js, leverages several key technologies:

  • JungleBusClient: Connects to JungleBus for real-time transaction data.
  • Axios: Handles HTTP requests to fetch associated content and transaction details.
  • FileSystem Promises: Manages file operations for storing state data.

Workflow:

  1. Listening for Locks: The script subscribes to new lock transactions using JungleBusClient.
  2. Evaluating Significance: Once a lock is identified, it checks if the locked amount meets the minimum threshold.
  3. Content Association: For significant locks, the script retrieves the locked content and the original poster’s details.
  4. State Recording: The transaction’s state, including relevant details like the amount locked and content information, is recorded in a file.

Implications and Use Cases

The script’s ability to identify and index significant lock transactions opens up various possibilities:

  • Content Discovery: Helps users discover high-value content validated by the community.
  • Social Signaling: Provides insights into what the community deems valuable or interesting.
  • Data Analysis: Offers data for researchers studying economic behaviors on the Bitcoin network.

The Code

First you’ll need a subscription to JungleBus that searches for locks.
To get the subscription visit https://junglebus.gorillapool.io/

Then create a new subscription, name it anything you like, and add ‘lock’ as a filter like the image below.

Once you have the subscription, you can run this little node app by putting your subscriptionID in at the bottom of the file where the subscription begins.

After creating the subscription and making this index.js file you’ll also need to install a few packages:

npm install @gorillapool/js-junglebus axios
//locksIndexer.js
const { JungleBusClient, ControlMessageStatusCode } = require("@gorillapool/js-junglebus");
const axios = require('axios');
const fs = require('fs').promises;
const path = require('path');
const filePath = path.join(__dirname, 'state.txt');

// Create an instance of axios with default configuration
const axiosInstance = axios.create({
// You can set base URLs, headers, timeout limits, etc.
});

// A simple in-memory cache to store responses
const responseCache = new Map();


// Function to append state to the file
const appendStateToFile = async (state) => {
try {
const stateString = JSON.stringify(state, null, 2) + '\n';
await fs.appendFile(filePath, stateString);
}
catch{console.log("Error writing to file")}

};

const client = new JungleBusClient("junglebus.gorillapool.io", {
useSSL: true,
onConnected(ctx) {
console.log("CONNECTED", ctx);
},
onConnecting(ctx) {
console.log("CONNECTING", ctx);
},
onDisconnected(ctx) {
console.log("DISCONNECTED", ctx);
},
onError(ctx) {
console.error("Error:", ctx);
},
});

const onStatus = function(message) {
if (message.statusCode === ControlMessageStatusCode.BLOCK_DONE) {
//console.log("BLOCK DONE", message.block);
} else if (message.statusCode === ControlMessageStatusCode.WAITING) {
console.log("WAITING FOR NEW BLOCK...", message);
} else if (message.statusCode === ControlMessageStatusCode.REORG) {
console.log("REORG TRIGGERED", message);
} else if (message.statusCode === ControlMessageStatusCode.ERROR) {
console.error("Error message:", message);
}
};

const onError = function(err) {
console.error("Error:", err);
};

const onMempool = function(tx) {
console.log("MEMPOOL TRANSACTION", tx);
};

const onPublish = async function(tx) {
let lockID = tx.id;
let lockedTo;
let lockerHandle;
let lockedToHandle;

// 1: We get a streaming feed of lock transactions.
// Generally the lock is in the _0 outout and the context (MAP DATA) is in the _1 output
// 2: Get information about all of the outputs in the transaction with a lock
//console.log("Repsponding to a new Lock with TransactionID: ", tx.id);
try {
let locksResponseData;
if (responseCache.has(tx.id)) {
locksResponseData = responseCache.get(tx.id);
} else {
const locksResponse = await axiosInstance.get(`https://locks.gorillapool.io/api/locks/txid/${tx.id}`);
locksResponseData = locksResponse.data;
responseCache.set(tx.id, locksResponseData); // Cache the response
}
// At this point the locksResponse should be an array of the outputs

// 3: Check if the lock is significant.
// If not - do nothing
// If it is then lets take a look at what they were locking to.
if (locksResponseData && locksResponseData.length > 1 && locksResponseData[0].satoshis > 1000) {
// 4: Retrieve the output with the data context
const hasContext = locksResponseData[1];
// 5: Check to make sure that there is map data in this output, and that it refrences another transaction
if (hasContext.data && hasContext.data.map && 'tx' in hasContext.data.map) {
let contentTx = hasContext.data.map.tx;
lockedTo = contentTx;
// 6: fetch the content of the referenced transaction
const contentResponse = await axiosInstance.get(`https://v3.ordinals.gorillapool.io/content/${contentTx}`, { responseType: 'arraybuffer' });
const contentType = contentResponse.headers['content-type'];

// If you want to you can check the Content-Type of the
// response before continuing.

// If it's text, it's safe to convert to string
const content = Buffer.from(contentResponse.data).toString();
// check to make sure its not an image.

const mapDataResponse = await axiosInstance.get(`https://locks.gorillapool.io/api/txos/${contentTx}_1`);
// NOTE: At this point we have the content to which the lock has been applied.
//. Details about the lock its self
lockerHandle = hasContext?.data?.map?.paymail ? hasContext.data.map.paymail : mapDataResponse.data.data.map.paymail;
lockedToHandle = mapDataResponse.data.data.map.paymail;
console.log(lockedToHandle, "got a lock of ", locksResponseData[0].satoshis, "satoshis from ", lockerHandle, "on:", content);
const state = {
lockTxid: tx.id,
satoshisLocked: locksResponseData[0].satoshis,
contentTxid: lockedTo,
postContent: content,
locks: locksResponseData,
postAuthor: mapDataResponse.data.data.map.paymail,
lockerHandle: lockerHandle
};
console.log(state);
await appendStateToFile(state);
}
}
} catch (error) {
console.error("Error handling published transaction:", error.message);
}
};


(async () => {
try {
await client.Subscribe(
"YOUR_SUBSCRIPTION_ID",
816000,
onPublish,
onStatus,
onError
);
} catch (error) {
console.error("Error during subscription:", error.message);
}
})();

Now, you can execute the file using a terminal, and start to watch the logs print what bitcoin is paying attention to.

node locksIndexer.js

Conclusion

Our Bitcoin locks indexing script stands as a testament to the innovative applications possible within the Bitcoin ecosystem. By tapping into the social signals inherent in lock transactions, it offers a unique window into the community’s interests and values. This script not only what bitcoin is thinking about but also opens new pathways for understanding and interacting with blockchain data.

This Blog Post Co-Authored by AI.
Follow me on Twitter for more: DevelopingZack

--

--

Zachary Weiner

Founder @MagicDapp.io & @AlphaDapp.com | Find @DevelopingZack on Twitter & Telegram