Doge Inucore v1.14.7-inu
Doge Inu on X
dogecoin/dogecoin → dogeinu/dogeinu-core

We forked Dogecoin Core and taught it to speak SPL.

Doge Inu Core is a hard fork of Dogecoin Core v1.14.7. We widened the transaction output to carry an asset commitment, added an ed25519 signature path next to secp256k1, and put a Tower BFT validator on top of the existing Scrypt auxiliary proof-of-work chain. The result is a Dogecoin-lineage UTXO chain that can hold, move, and mint Solana Program Library tokens natively.

Read the protocol docsLaunch a token

6,482 commits ahead of upstream · 214 contributors · AGPL-3.0

~/src/dogeinu-corebash — 96×24
block height
5,417,392
block time (60-blk avg)
58.6 s
sustained tps
14,880
spl assets minted
38,114
median fee
0.0104 Ð
validators
1,204
the problem

Dogecoin has the culture. Solana has the token standard. Nobody had both.

Dogecoin Core is a 2013 fork of Litecoin, which is itself a fork of Bitcoin Core. It inherits the UTXO model, the Scrypt proof-of-work, and — critically — a transaction output structure that has no room in it for anything other than a value and a locking script. There is no asset field. There is no mint authority. Every attempt to put tokens on a Bitcoin-lineage chain has therefore been an overlay: colour the coins, index them off-chain, and pray the indexers agree.

Overlays break at exactly the moment they matter. Because consensus does not know about the asset, consensus cannot reject an invalid transfer of it. Two indexers can hold two different opinions about who owns what and both are, from the chain's point of view, correct. That is not a token standard. That is a shared spreadsheet with extra steps.

Solana solved the other half. The SPL Token program is a genuine standard: mint accounts, associated token accounts, a deterministic address derivation, a decimals field, freeze and mint authorities, and a program that the runtime actually enforces. What it does not have is a merge-mined Scrypt chain with a decade of Dogecoin behind it.

Doge Inu closes the gap at the consensus layer rather than above it. We did not build a bridge. We changed the transaction format.

src/primitives/transaction.hupstream — before the fork
class CTxOut
{
public:
    CAmount nValue;
    CScript scriptPubKey;

    CTxOut() { SetNull(); }
    CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn);

    ADD_SERIALIZE_METHODS;

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action) {
        READWRITE(nValue);
        READWRITE(*(CScriptBase*)(&scriptPubKey));
    }
};
src/primitives/transaction.hdogeinu-core — after a91f2c3
class CTxOut
{
public:
    CAmount nValue;
    CScript scriptPubKey;
    // 32-byte commitment to the SPL mint this output is denominated in.
    // All-zero means the output is native DINU and serialises exactly as
    // upstream, which is what keeps old blocks valid under the new rules.
    uint256 assetCommitment;
    uint64_t assetAmount;

    ADD_SERIALIZE_METHODS;

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action) {
        READWRITE(nValue);
        READWRITE(*(CScriptBase*)(&scriptPubKey));
        if (s.GetVersion() >= INU_ASSET_VERSION) {
            READWRITE(assetCommitment);
            READWRITE(assetAmount);
        }
    }

    bool IsNativeOnly() const { return assetCommitment.IsNull(); }
};
architecture

Three planes, one chain

The node keeps the Dogecoin settlement plane untouched and bolts two new planes beside it. Each plane has its own validity rules, and a block is only valid if all three agree.

planeinherited fromwhat it decidesfailure modeblock budget
settlementdogecoin/dogecoin v1.14.7UTXO validity, Scrypt PoW, auxpow header, 60 s targetchain reorg1,000,000 B
assetsolana-labs/solana-program-librarymint supply, authority checks, ATA derivation, decimalstransaction rejected4,194,304 CU
voteanza-xyz/agave (Tower BFT)finality, slashing, stake-weighted lockoutsfinality stall64 slots

The asset plane runs inside script

Rather than embedding a full BPF runtime in a C++ node, we compiled the subset of the SPL Token program that has consensus meaning into two new script opcodes. OP_SPLMINT takes a mint account commitment, an amount, and a signature from the mint authority. OP_SPLXFER takes a source commitment, a destination, and an amount, and enforces conservation.

Both opcodes were previously OP_NOP7 and OP_NOP8, which is how the change stays soft-forkable for relay purposes even though the serialisation change makes it a hard fork for validation. Nodes that have not upgraded see two no-ops and a value-zero output, and they do not crash. They are simply wrong about the balance, which is why the activation height is enforced by the version bits in versionbits.cpp.

Conservation is checked in the same loop that already checks nValueIn >= nValueOut. The asset amounts are summed per commitment into a flat map, and any commitment whose inputs do not cover its outputs fails the block, not just the transaction.

src/consensus/tx_verify.cppasset conservation
bool Consensus::CheckAssetConservation(const CTransaction& tx,
                                       const CCoinsViewCache& view,
                                       CValidationState& state)
{
    std::map<uint256, uint64_t> in, out;

    for (const CTxIn& txin : tx.vin) {
        const CTxOut& prev = view.AccessCoin(txin.prevout).out;
        if (prev.IsNativeOnly()) continue;
        uint64_t& acc = in[prev.assetCommitment];
        if (acc > std::numeric_limits<uint64_t>::max() - prev.assetAmount)
            return state.DoS(100, false, REJECT_INVALID, "asset-in-overflow");
        acc += prev.assetAmount;
    }

    for (const CTxOut& txout : tx.vout) {
        if (txout.IsNativeOnly()) continue;
        out[txout.assetCommitment] += txout.assetAmount;
    }

    for (const auto& [commitment, amount] : out) {
        const uint64_t supplied = in.count(commitment) ? in.at(commitment) : 0;
        if (supplied == amount) continue;

        // A shortfall is only legal if this transaction carries a valid
        // OP_SPLMINT for exactly the difference, signed by the authority
        // recorded in the mint account at its current height.
        if (amount > supplied && HasValidMintAuthorisation(
                tx, commitment, amount - supplied, view))
            continue;

        return state.DoS(100, false, REJECT_INVALID, "asset-not-conserved",
                         false, strprintf("commitment=%s in=%d out=%d",
                                          commitment.ToString(), supplied, amount));
    }
    return true;
}
validator/src/tower.rsinu-validator — vote anchoring
/// A Doge Inu vote is only meaningful if it names a settlement block that
/// actually carries the Scrypt work it claims. Tower BFT gives us fast
/// finality; the auxpow header is what makes that finality expensive to lie
/// about, so every vote carries both and we refuse to count one without the
/// other.
pub fn record_vote(
    &mut self,
    slot: Slot,
    anchor: AuxPowHeader,
    identity: &Pubkey,
) -> Result<Lockout, TowerError> {
    let stake = self.stakes.get(identity).copied().unwrap_or(0);
    if stake < MIN_VOTE_STAKE {
        return Err(TowerError::InsufficientStake { have: stake });
    }

    if !anchor.verify_scrypt_pow(self.network.pow_limit)? {
        return Err(TowerError::AnchorWorkInvalid(anchor.hash()));
    }

    if anchor.height < self.last_anchor_height {
        return Err(TowerError::AnchorRegression {
            got: anchor.height,
            last: self.last_anchor_height,
        });
    }

    let lockout = Lockout::new(slot, self.confirmation_count(slot));
    self.votes.push_back((lockout, anchor.hash()));
    self.last_anchor_height = anchor.height;

    // 31 lockouts deep is the point at which rolling back costs more than
    // the cumulative Scrypt work anchored beneath it.
    while self.votes.len() > MAX_LOCKOUT_HISTORY {
        let (rooted, _) = self.votes.pop_front().expect("non-empty");
        self.root = Some(rooted.slot());
    }

    Ok(lockout)
}
the validator

We wrote a validator so finality does not take an hour

Dogecoin gives you a 60-second block and probabilistic finality. For a payments meme coin that is fine. For a token layer where someone is swapping a freshly minted asset against a pool, six confirmations of waiting is unusable.

inu-validator is a Rust client that runs alongside dogeinud. It does not produce blocks — miners still do that, and they still merge-mine against Litecoin. What it does is vote. Validators stake DINU, watch the settlement plane, and cast Tower BFT votes on the slot ordering. Once a slot accumulates two-thirds of stake weight behind it, the asset plane treats it as final and the explorer marks it green.

The interesting constraint is that a vote must name an auxiliary proof-of-work header it is anchored to. A validator cannot vote for a fork with no work behind it, which means an attacker who buys two-thirds of stake still has to out-mine Litecoin's hashrate to produce a chain their stake can validly vote for. Stake buys you speed. Work still buys you history.

MIN_VOTE_STAKE
250,000 DINU, locked for a 14-epoch cooldown
MAX_LOCKOUT_HISTORY
31 votes, doubling lockout from 2 to 2³¹ slots
slot duration
400 ms — 150 slots per settlement block
time to finality
1.8 s typical, 12.4 s p99 under partition
slashing
100% of stake for equivocation, 0.5% per 1,000 missed slots
end to end

Minting an asset, from a cold node

The session below is the whole loop: sync a node, create a mint, send a transfer, and read the balance back out of the UTXO set. No indexer involved at any point — every number comes out of consensus state.

terminal session — devnetcaptured 2026-09-19
$ dogeinud -daemon -splindex=1 -printtoconsole
Doge Inu Core Daemon version v1.14.7-inu (64-bit)
Loading block index... done (4.21s)
Verifying asset commitments for last 288 blocks... done (0.88s)
Progress: 100.00% | height=5417392 | tip=00000000000000ba4f2e...c19d
Asset plane: 38114 mints, 1284551 token accounts, 0 conservation faults

$ dogeinu-cli createsplmint '{"decimals":6,"supply":1000000000,"name":"WOWCOIN","symbol":"WOW"}'
{
  "mint": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PB5wBQZ",
  "authority": "DiNuQq8xYx4FhVvJ2rC1mA6KeT9sW3uB7nZpLk5gH4dR",
  "txid": "4f1a2e9c7b0d3856a1cf42be09d7315e8a6c2f0b4d91e73a5c8f206bd14e97a3",
  "vout": 0,
  "confirmations": 0,
  "fee": "0.01040000"
}

$ dogeinu-cli sendspl 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PB5wBQZ \
    DRip3nWq5xAe2bK8vJ1cH6uT4mS9yZ7fL0gQpN3dXwEo 42000.5
4b8e1f60c3a27d95e0148fb6297ca35d81e70b4f6a2d93c58017ef4ba62d0c91

$ dogeinu-cli getsplbalance DRip3nWq5xAe2bK8vJ1cH6uT4mS9yZ7fL0gQpN3dXwEo
{
  "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PB5wBQZ": {
    "symbol": "WOW",
    "amount": "42000.500000",
    "decimals": 6,
    "utxos": 1,
    "finalized": true,
    "finalized_by_slot": 812608850
  }
}

$ dogeinu-cli verifyassetstate 5417392
{ "commitments": 38114, "conserved": true, "root": "e70b...44c1", "elapsed_ms": 611 }
why this matters

verifyassetstate recomputes every asset balance from the UTXO set and compares the root against the one committed in the block header. If an overlay protocol ran that command it would have nothing to compare against, because there is nothing in the header. Here, a mismatch is a consensus failure and the node halts.

measured, not modelled

What the fork costs

Numbers from a 24-hour soak on 12 bare-metal nodes (AMD EPYC 9354P, 384 GB, NVMe), 40 Gbit between regions, replaying mainnet traffic at 4× with synthetic asset load mixed in.

Doge Inu Core v1.14.7-inu vs. Dogecoin Core v1.14.7, same hardware
metricdogecoin coredoge inu coredeltanote
initial block download6 h 12 m7 h 04 m+13.9%asset commitment verification
block validation, p5041 ms58 ms+41.4%conservation pass over vout
block validation, p99310 ms402 ms+29.7%worst case is a 900-output mint batch
UTXO set on disk7.9 GB11.4 GB+44.3%40 extra bytes per non-native output
time to finality~60 min1.8 s−99.9%Tower BFT, 6 conf equivalent
sustained tps3314,880+45,000%asset plane is not block-size bound
mempool RSS at 300k tx1.1 GB1.6 GB+45.5%commitment index is held hot

We are slower at everything a Dogecoin node already did, and we think that is the correct trade. A 41% increase in block validation time on a 60-second block is 17 milliseconds of a 60,000 millisecond budget. In exchange the chain gained a token standard that consensus enforces and a finality time a human can wait through. Full methodology and raw traces.

so, mint something

The launcher is open to anyone with a wallet

Connect a Solana wallet, pick a name, a symbol, a decimals value and a supply, and the launcher builds and sends a real SPL mint transaction. It defaults to devnet, where the airdrop is free and the mistakes are cheap. Switch the cluster in the wallet menu when you actually mean it.

You keep the mint authority unless you choose to revoke it during creation. Revoking is irreversible and is the only thing on this site that cannot be undone, so the launcher asks twice.

Open the launcherWatch the chain
dogeinu-cli — one-linerthe short version
# Mint, fund, and renounce in a single batch. The node builds one
# transaction, so either all three land or none of them do.
$ dogeinu-cli batch <<'EOF'
createsplmint  {"decimals":9,"supply":420690000000,"symbol":"INU"}
sendspl        $MINT $TREASURY 420690000000
revokeauthority $MINT mint
EOF

txid     b19c7f4a0d62e835... ( 3 ops, 412 vbytes )
fee      0.01820000 DINU
status   finalized in 1.61s at slot 812608912