JavaScript プログラミング

<input type=”date”>を使いやすく

Published by:

普通に <input type="date" placeholder="日付を選択してください" value=""> と書くと、

日付の選択:



となって、placeholderも効かないし、「yyyy/mm/dd」なんて表示されてもどうなの?となりますよね?

そこで、<input type="text" onfocus="this.type='date'" onblur="this.type='text'" placeholder="日付を選択してください" value=""> としてフォーカスが付いたときだけ type=”text” を type=”date” に切り替えるというミニテクニック。

日付:




さらに、<input type="text" onfocus="this.type='date'" onblur="if(!this.value)this.type='text';" onchange="if(!this.value)this.type='text';" placeholder="日付を選択してください" value=""> のようにして、選択済みならtype属性を戻さないのもありかも。


発表、ニュース

Java版マインクラフトの地形データをエクスポート&インポートするには?

Published by:

Minecraftで地球を再現しよう!というプロジェクト「Terra 1 to 1」。

リアルな地形データを使って、地球まるごとマイクラワールドにしてしまうという壮大なプロジェクトです。
Java版のForgeプラグインとして実装されており、必要なプラグインを集めるのはちょっと面倒ですが、ダウンロードしてインストールするだけなので、Forgeで遊んだことがある人なら普段どおりの手順で起動することが出来ます。

/tp コマンド(テレボート。指定座標に移動)の代わりに /tpll コマンドで緯度経度を指定したテレポートも出来るので、Googleマップで座標を確認すれば世界中のどこにでもマイクラ内で移動することが出来ます。すごい!

ただ、いかんせんJava版ですので、私が普段遊んでいるiPadのマイクラアプリ(統合版)ではプレイできず、インストールして「へーすごいー」となっただけ。このワールドをエクスポートして統合版で遊びたい!と思いつき、実際にやってみることにしました。

マイクラのデータをエクスポート&インポートするには、WorldEditというプラグインが使えるのですが、試したところせいぜい300×300ブロック程度の入出力が出来る程度で、私が住んでいる沼島(Terra 1 to 1 ワールド内の座標でいうと「3418200 -6 13483250」 ~ 「3415300 130 13480800」の範囲)である3,000×3,000ブロックを入出力しようとするとメモリエラーになってしまう・・・。

300×300ブロックに分割して入出力しようと考え、WorldEditプラグインを拡張し、/worldexport、/worldimportという2つのコマンドを実装しました。WorldEditのコードをフォークして、GitHubにもプッシュしています。

改造版のWorldEditのjarはこちら

この2つのコマンドは、schematic形式のファイルを分割エクスポート&インポートするプログラムです。
使い方はGitHubのページに記載したとおりで、「/worldexport x1 y1 z1 x2 y2 z2 max_block_length」コマンドでエクスポート、「/worldimport [from] [to]」でインポートできます。

かなりの時間がかかるのと、たっぷりのメモリ(16GB以上推奨)が必要になります。私の場合、取り込み側の起動構成は「-Xmx24G -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1NewSizePercent=20 -XX:G1ReservePercent=20 -XX:MaxGCPauseMillis=50 -XX:G1HeapRegionSize=32M」でした。

インポートする場合、「Terra 1 to 1」の起動構成とは別に分けたほうが良いです。「Terra 1 to 1」で使っているプラグインがさらにメモリを使いますので、schematicをインポートする専用の起動構成を分けたほうが、まだスムーズに行けるかと思います。

インポートするワールドはスーパーフラットで作り、私の場合は島だったので「Water World」のプリセットを用意しました。
元のプリセットのままだと海が深すぎて高さ分の処理負荷がかかるため、浅めの海(3;minecraft:bedrock,3minecraft:stone,2minecraft:dirt,2minecraft:sand,6minecraft:water;24;oceanmonument,biome_1・・・水は高さ6、それ以外の要素も低く設定。)を作成。

実行したコマンドは、

■エクスポート@「Terra 1 to 1」のワールド
/tp @p 3418200 0 13483250
/worldexport 3418200 -6 13483250 3415300 130 13480800 200

■インポート@新規ワールド(「Water World」プリセットのスーパーフラット)
/tp @p 0 8 0
/worldimport

エクスポートは30分もあれば終わるのですが、インポートはなんと12時間もかかりました。
特にY軸の位置合わせを何度も間違えたので、最初は小さめのワールドで確認したほうが間違いないかと思います。

無事インポートできたら、MCCToolChest PE (Bedrock version)で統合版ワールドに変換します。
これも結構時間かかりましたが、完璧な精度で変換できたので文句なし!です。

↓がインポートした沼島。船着き場が海に沈み込んでいるのは、海面位置のバグがあるのかなと思っていましたが、もともと船着き場あたりは埋立地だったことを思いだしました。

「Terra 1 to 1」では地形データに「Terrain Tiles」というオープンデータを使っていて、そこには「A global dataset providing bare-earth terrain heights」と説明書きがありましたので、ある意味正確に元の地形が再現されているのだと思います。

マインクラフト統合版の沼島ワールドも公開しておきます。
もしよかったらダウンロードして探索してみてください。

マインクラフト統合版の沼島ワールド

これから船着き場やおうち、その他もろもろを作っていこうと思います。

日常 沼島 発表、ニュース

コープ受け取りin沼島

Published by:

Android8&9の検証用に購入したHUAWEI nova light 3の撮影テスト。

北区の浜
橋本商店
弁天さん
コープを受けとる汽船場。
すぐいくと混雑してるので、少し遅めにいってます。
今日は天気がよく、淡路島もきれいに見えます。
沼島いち、コープを利用してるんじゃなかろうか。
発表、ニュース

アプリ版「風来のシレン」のアイテム値段表

Published by:

識別用のチェックリストつき!(カッターナイフで切ってね)

近所の友人にすすめられ、ぬけぬけと「不思議のダンジョン 風来のシレン」というゲームアプリをiPadにインストールしたのが最後、すっかりハマってしまいました。

トルネコの冒険を含め、これまで不思議のダンジョン系はまったく手を出したことがなく、不惑にしてその面白さ、中毒性に侵されています。

最近「フェイの最終問題」という一番難しいダンジョンに挑戦しており、必要にかられてA4一枚で確認できる値段表を作成しました。

※値段は「値段による判別表/DS/風来のシレン攻略 – ドラクエ&パズル」を参考にしました。

検索すると同じようなPDFを公開されている方がたくさんいたのですが、アイテム識別済みのチェックリストが欲しかったので、自作してしまいました。

とにかく恐ろしいゲームですので、誰にもおすすめできません。数独以上に時間が奪われていきます・・・!

発表、ニュース

統合版マインクラフト用サーバーで複数ワールド起動

Published by:

MINECRAFT 用サーバーソフトウェア (アルファ版) をダウンロード
から統合版(Bedrock Edition/BE)用のサーバーをダウンロードできます。

このサーバーは今のところNintendo Switchからはアクセス出来ませんが、それ以外のAndroid、iPad、PC用などの統合版マインクラフトからアクセスでき、「親機」を用意せずともいつでも誰でもマルチプレイのワールドへアクセスできてとても便利です。 (Java Editionからのアクセスもだめ)

まだアルファ版ということもあり、プラグインが使えなかったりチートコマンドが全部は使えなかったりしますが、省メモリでかなり安定動作しているので、とにかくおすすめです。

インストール方法はMinecraft:Bedrock Edition用の公式バニラサーバーを開いてみたに詳しく説明してくれています。

一つ個人的に問題があって、一つのインストールディレクトリで複数ワールドをサーブすることができないんです。
仕方がないので複数ディレクトリにコピーして、ポート番号を変えて運用することになりますが、起動スクリプトなんかを用意するのが面倒・・・ということで、インストールディレクトリ名を screen コマンドのスクリーン名にして起動・終了・再起動・screenアクセスするスクリプトを作成しました。

bedrock-shells.zip

access.sh・・・起動後のシェルへアクセスできます
restart.sh・・・crontab登録用。バックアップ処理も行います(参考
start.sh・・・起動スクリプト
stop.sh・・・終了スクリプト

小学生の息子や近所の友達ともかくれんぼや秘密基地作り、探検をして楽しんでます!

ビットコイン ブロックチェーン プログラミング

Ledger Nanoの受取アドレスを生成するプログラムを作った

Published by:

朝からこんな怖い話がニュースになっていました・・・!

レッジャー社のハードウェアウォレットに脆弱性 「中間者攻撃」で資金流出リスク

 レッジャー社がツイッターで参照する匿名レポートによると、マルウェアに感染したパソコンを使ってレッジャー中にビットコイン受取アドレスを生成しようとする際、「中間者攻撃」とよばれるセキュリティ侵害が起こる可能性がある。マルウェアがアドレス生成のためのコードを改編し全ての入金がハッカーに送られてしまうという。

普通のディスプレイ付きLedger Nanoであればこのサイトで紹介されている方法でアドレスが正しいかどうかを判定できますが、私が持っている古いディスプレイなしのタイプでは確認できません。

というわけで確認用のツールを作ってみました。

Ledger wallet address checker
http://hello.lumiere-couleur.com/app/browser_ledger/

browser_ledger

サーバー生成ではありませんので、ネットワークを切断した状態でも使えます。
というか、そのように使った方がいいかと思います。

TypeScript+Browserify+jQueryで作りましたが、意外に簡単なんですね。
ちょっとしたツールだったら、これからこの方法で行こうかな。

Github リポジトリはこちら

料理 日常

七輪にアダプターを付けたらすごく便利になったよ!

Published by:

1、2月に灘の炭焼き見学&お手伝いで大量の炭をいただいたので、今年の冬はふんだんに火鉢を稼働しています。やっぱり炭はええのう。。
火鉢があると豆炭あんかの豆炭を熾すのも簡単で、とにかく便利です。
豆炭を単体で火をつけるのはなかなか手間ですが、火鉢があるとたとえ消えかけの炭でも確実に真っ赤っかに熾してくれます。

炭と言えば、火鉢以外にも七輪・バーベキューコンロで魚介やお肉を焼くのが定番ですが、うちでは主に焼きそば、焼き芋、おでん、お魚を焼くのに重宝しています。

まず基本形。

まず基本形。

鋳物プレートを置くと、美味しい焼きそばが作れます。

鋳物プレートを置くと、美味しい焼きそばが作れます。

アダプター(七輪上置き)を併用すると、上に鍋を置きやすい!! むしろ無いときは毎回面倒な思いをしていました。

アダプター(七輪上置き)を併用すると、上に鍋を置きやすい!!
むしろ無いときは毎回面倒な思いをしていました。

我が家にある一番大きな鍋でも十分に乗せられます。

我が家にある一番大きな鍋でも十分に乗せられます。

残念ながら、上の写真に写っている鋳物プレート(イワタニのCB-P-OM2)は製造中止になっていたようで、代替えはフッ素コーティングのフライパンみたいのになってました。多分炭では使えない・・・
この大きさだとちょうどプレート裏の四本足が七輪にジャストマッチして、超便利なんですけどね。
もしホームセンターで売れ残ってたら即買い必死のおすすめアイテムです。

アダプター(七輪上置き)はつい最近買い足したのですが、それまではむりやり石や鉄の棒を使ってかさ上げして鍋をのっけてました。
七輪の上にどんと鍋を置くと空気の流れが悪くなるし、多めの炭を使うと上にはみだして鍋底を汚すしで、とにかく手間ひまかかってました。
ですが、アダプター購入後は気楽に干物を炙ったり、余った炭で晩飯を作ったりと普段使い可能なまでに重宝するようになっています。

わが家にはもう一つ、「イモッ子もちっ子」という商品があって、これも昔Amazonで買ったはずですが、今は売ってませんね・・・
やっぱり買う人いないんでしょうか。

熊本の実家にもあった焼きいも器、イモッ子もちっ子。美味しく出来ますよー。

実家にもあった焼きいも器。美味しく出来ますよー。

七輪の使い方は、他にもお家ごとにいろいろ良い方法がありそうですが、もしおすすめの使い方なんかあったらコメント下さいませ。

ついでに炭おこしの写真も撮りましたので、書いときます。

着火剤代わりの松ぼっくりを用意します。煙と松ヤニのにおいがしますので、民家の多い地域では使いにくいかも。。

着火剤代わりの松ぼっくりを用意します。煙と松ヤニのにおいがしますので、民家の多い地域では使いにくいかも。。

火おこし器のフタに松ぼっくり三つと半分に切った新聞三枚をのっけます。

火おこし器のフタに松ぼっくり三つと半分に切った新聞三枚をのっけます。

炭を入れる本体にも、底に松ぼっくりを5個ほど。この火おこし器は三年使ってますが雨ざらしにしたこともあって底が抜けました。今は小さなステンレス網(135x135mm)をしいてしのいでます。

炭を入れる本体にも、底に松ぼっくりを5個ほど。この火おこし器は三年使ってますが雨ざらしにしたこともあって底が抜けました。今は小さなステンレス網(135x135mm)をしいてしのいでます。”

その上に炭を必要なぶん乗っけます。

その上に炭を必要なぶん乗っけます。

四枚目の新聞に火をつけ、フタに乗っけている三枚の新聞紙に火をつけます。

四枚目の新聞に火をつけ、フタに乗っけている三枚の新聞紙に火をつけます。

急いで最初の新聞紙を乗っけて・・・

急いで最初の新聞紙を乗っけて・・・

本体をムギュッと上にのせます。はみ出た新聞紙なんかは押し込みます。 炭の量が多い時は、木の切れはしなんかも下に入れておくといいです。

本体をムギュッと上にのせます。はみ出た新聞紙なんかは押し込みます。
炭の量が多い時は、木の切れはしなんかも下に入れておくといいです。

三和金属 しちりん上置き27cm SU-27
三和金属 (2013-12-27)
売り上げランキング: 23,892
EBM 18-8 スーパーストロング 丸 焼網 270型
EBM
売り上げランキング: 89,959
キャプテンスタッグ 大型火消し壺 火おこし器セット  バーベキュー用
キャプテンスタッグ(CAPTAIN STAG) (2012-03-09)
売り上げランキング: 1,497
ビットコイン ブロックチェーン プログラミング 発表、ニュース

ビットコインのウォレットと、支払い用のプログラムを作ろう

Published by:

ビットコインのウォレットは数多くありますが、ハードウェアウォレットは高いしなあ・・・という方は、とりあえずウォレットアプリを利用するのがおすすめです。
ウォレットアプリだと、スマホが壊れたときや新しいスマホに買い替えたときに大事なビットコインがなくなっちゃうんじゃない?なんて心配になりますが、大丈夫!
すべてのビットコインはブロックチェーン上に保管されていて、そのブロックチェーンは世界中に遍在しています。

ハードウェアウォレットだろうとペーパーウォレットだろうと、もちろんウォレットアプリも同じですが、すべてその中にビットコインはありません!
これらウォレットはブロックチェーンに保管されているビットコインへの鍵のようなもので、仮にウォレットがぶっ壊れてもビットコインが失われたわけではなく、合鍵を使ってアクセスすればいいのです。

合鍵となるのがリカバリーフレーズですが、最初にウォレットを作るときに必ずメモを残すようにしましょう。
また、このリカバリーフレーズさえあればだれでもそのウォレットへアクセスできますので、十分気をつけて保管して下さい。

私が使っているCopayというウォレットアプリでは、12の日本語の単語(ひらがな)でリカバリーフレーズが作れます。
仮に私がCopayでウォレットを1つ作り、そのリカバリーフレーズを人にあげれば、それはそのまま相手のウォレットにもなっちゃいます。
この仕組み、本当に面白いですよね・・・

最近個人的に作っているプログラムでビットコインの支払いを受け付ける必要があったのですが、HDウォレット形式に対応することで毎回支払いのビットコインアドレスを変えることが出来ます。
こうすることでちゃんと支払われたかどうかをプログラムでチェックできるのでおすすめです。
1つのアドレスに全部送ってもらうようにすると、どれが誰のものか分からなくなってしまうんですよね。

下はTypeScript版のコードですが、これで新しい受取用のアドレスを生成することが出来ます。
Wallet.tsの「const mnemonic = ‘リカバリーフレーズ’;」のところに、Copayで生成したウォレットのリカバリーフレーズを入れます。

import bitcoin = require('bitcoinjs-lib');
import bip39 = require('bip39');

const mnemonicToM = (mnemonic, password, network) => {
    const seed = bip39.mnemonicToSeed(mnemonic, password || "")
    const m = bitcoin.HDNode.fromSeedBuffer(seed, bitcoin.networks[network || "bitcoin"])
    return m
}
const mnemonic = 'リカバリーフレーズ';

/**
 *  ビットコインウォレット
 */
export class Wallet {
  /**
   *  支払い用アドレス生成
   */
  public static createPaymentAddress(tx_index: number) {
    const m = mnemonicToM(mnemonic, '', 'bitcoin');  // 秘密鍵
    // console.log(m.derivePath("m/44'/0'/0'").toBase58()); // xpriv...
    // console.log(m.derivePath("m/44'/0'/0'").neutered().toBase58());  // xpub...
    // console.log(m.derivePath("m/44'/0'/0'/0/0").getAddress()); // 1DQ...

    let paymentAddress = m.derivePath("m/44'/0'/0'/0/" + tx_index).getAddress();
    return paymentAddress;
  }
}

次に、受取チェックのプログラム。

import blockexplorer = require('blockchain.info/blockexplorer');
import rp = require('request-promise-native');

import { BitcoinAddress } from "./BitcoinAddress";
import { BitcoinTransaction } from "./BitcoinTransaction";

/**
 *  ビットコイントランザクション
 */
export class Transaction {
  /**
   *  最新のブロック総数を取得
   */
  public static getBlockCount(): Promise<number> {
    let url = 'https://blockchain.info/ja/q/getblockcount';
    return rp(url)
      .then((body: string) => {
        let count = parseInt(body.trim());
        if (isNaN(count)) {
          return -1;
        }
        return count;
      });
  }

  /**
   *  指定のアドレスの情報を取得
   */
  public static getAddress(address): Promise<BitcoinAddress> {
    // 最新のブロック総数を取得
    return Transaction.getBlockCount()
      .then((count: number) => {
        // アドレス情報を取得
        return blockexplorer.getAddress(address)
          .then((result) => {
            let bitcoinAddress = new BitcoinAddress();
            bitcoinAddress.address = address;
            bitcoinAddress.total_received = result.total_received / 100000000;
            bitcoinAddress.last_confirmation = 0;
            bitcoinAddress.txs = [];

            // トランザクションの承認数を確認
            if (result.txs && result.txs.length > 0) {
              for (let i = 0; i < result.txs.length; i++) {
                let tx = result.txs[i];
                let bitcoinTransaction = new BitcoinTransaction();
                bitcoinTransaction.block_height = tx.block_height ? tx.block_height : count;
                bitcoinTransaction.confirmation = Math.max(0, count - tx.block_height);
                bitcoinTransaction.confirmation = !isNaN(bitcoinTransaction.confirmation) ? bitcoinTransaction.confirmation : 0;
                if (bitcoinAddress.last_confirmation == 0) {
                  bitcoinAddress.last_confirmation = bitcoinTransaction.confirmation;
                } else {
                  bitcoinAddress.last_confirmation = Math.min(bitcoinAddress.last_confirmation, bitcoinTransaction.confirmation);
                }
                bitcoinTransaction.btc_amount = 0;
                for (let j = 0; j < tx.out.length; j++) {
                  let out = tx.out[j];
                  if (out.addr == address) {
                    bitcoinTransaction.btc_amount += out.value / 100000000;
                  }
                }
                bitcoinAddress.txs.push(bitcoinTransaction);
              }
            }

            return bitcoinAddress;
          });
      });
  }
}
import { BitcoinTransaction } from "./BitcoinTransaction";

/**
 *  ビットコインアドレス
 */
 export class BitcoinAddress {
   public address: string;
   public total_received: number;
   public last_confirmation: number;
   public txs: BitcoinTransaction[];
 }
/**
 *  ビットコイントランザクション
 */
 export class BitcoinTransaction {
   public block_height: number;
   public confirmation: number;
   public btc_amount: number;
 }

実行するとこんな感じです。

import { BitcoinAddress } from "bitcoin/BitcoinAddress";
import { Transaction } from "bitcoin/Transaction";
import { Wallet } from "bitcoin/Wallet";

// tx_indexは0スタートのインクリメント値
let paymentAddress = Wallet.createPaymentAddress(0);
console.log('paymentAddress', paymentAddress);

Transaction.getAddress(paymentAddress)
  .then((bitcoinAddress: BitcoinAddress) => {
    console.log('bitcoinAddress', bitcoinAddress);
  });

$ npm run wallet_test 

> myprogram@1.0.0 wallet_test /home/xxxxxxxxx/myprogram
> env NODE_PATH=./build node ./build/wallet_test.js

paymentAddress 1rUTG3jWJ3rkEiKFLyvXbMaQcLnqxr49c
bitcoinAddress BitcoinAddress {
  address: '1rUTG3jWJ3rkEiKFLyvXbMaQcLnqxr49c',
  total_received: 0.0104252,
  last_confirmation: 1508,
  txs: 
   [ BitcoinTransaction {
       block_height: 492652,
       confirmation: 1508,
       btc_amount: 0.0104252 } ] }

生成された支払い法のアドレス(上の実行例では1rUTG3jWJ3rkEiKFLyvXbMaQcLnqxr49c)が、Copayで表示される受取用アドレスと一致してればOK。
または、Copayの設定画面で確認できる、ウォレットの拡張公開鍵と、Wallet.tsでコメントアウトしている「// console.log(m.derivePath(“m/44’/0’/0′”).neutered().toBase58()); // xpub…」のコメントを外して実行してみて、その出力結果が一致しているかどうかも確認した方がいいでしょう。

ソースのダウンロードはこちら

ビットコイン ブロックチェーン

Ledger NanoでSegwitに切り替えたら残高なくなってて焦った

Published by:

ビットコインを受け取ろうと、ハードウェアウォレットのLedger Nano(過去の記事「ビットコインを普通のお財布に入れてみた」)をPCに挿したところ、Segwit対応のファームウェアにアップデートできるとのことで、アップデートしました。
いったん全部消してリカバリーするのでドキドキするのですが、8月のビットコインキャッシュ誕生の時にも一度やっていたので同じようにアップデートしました。

アップデート後にLedger Nanoを挿し直して、Segwit切り替えの確認画面が出たのでSegwitを選んだところ、残高が 0 BTCになってる!?
確認画面をよく見たらアドレスが変わるので、旧ウォレットから新ウォレットへ送金しないといけないそう。
設定画面から旧ウォレットに戻して新ウォレットのアドレスへ全額送信して、ちゃんと移動できました。
あ~、びっくりした。

Segwitへの切替画面。 後からいつでもスイッチ可能です。

Segwitへの切替画面。
後からいつでもスイッチ可能です。

SETTINGSの右下のBLOCKCHAINSをクリック

SETTINGSの右下のBLOCKCHAINSをクリック

BITCOINを選択して、次の画面(最初の写真と同じ)でLEGACYをクリックすると旧ウォレットが開きます。

BITCOINを選択して、次の画面(最初の写真と同じ)でLEGACYをクリックすると旧ウォレットが開きます。

旧ウォレットから新ウォレットへ全額移動。焦らせるぜ・・・

旧ウォレットから新ウォレットへ全額移動。焦らせるぜ・・・

Ledger Nanoは品切れで、より高機能なLedger Nano Sも売ってるけど、高い・・・。
見た目もLedger Nanoの方が好きなんだけどな。

ビットコイン ブロックチェーン

インターネットはcopy、ブロックチェーンはmove

Published by:

ブロックチェーンやビットコインについての記事を度々見かけるようになりましたが、技術的な説明が全面に出てきてしまうので、結局何?という感じの人が多いのではないでしょうか。

一昨日の朝日新聞にもブロックチェーンの記事があり、最近読んだ中では一番分かりやすかったのですが、横からのぞき込んできた小学三年生の息子に「ブロックチェーンって何?」とシンプルに質問されて、思わずたじろいでしまいました。

その時は「インターネットは調べ物をしたりするときに使うけど、ブロックチェーンはいろんなものを交換するのに使うんだよ」と答えましたが、頭のなかでは何かうまく言えてないなあと息子の問いが残ったままになっていました。

あれから考え続けてやっと一つ分かりやすい言い回しを思いついたのが、タイトルの「インターネットはcopy、ブロックチェーンはmove」です。

インターネットブラウザで「ソースを見る」をしたことがある人は、最初びっくりしたのではないでしょうか。
どこかのWebサイトのページを表示して、「ソースを見る(Ctrl-U)」でHTMLソースを表示して、ブラウザによっては編集も出来て、しかも編集した内容がブラウザにも反映されてしまうと、もしかしてハッキングしちゃった??なんてドキドキしたりして。

実際はブラウザで表示しているのは、Webサイトのサーバーに置いてある実体のコピーなので、サーバーの元ファイルを書き換えることは出来ません。
私も昔、そこでやっとインターネットの仕組みについて理解しました(なーんだコピーを見てるんだー)。

ブロックチェーンは問答無用でmove(移動)します。
地上波放送の「ダビング10(9回まではDVDにコピーできるけど、10回目は元ファイルが消えてしまう仕組み)」の最後のムーブだけみたいな感じです。

オンライン銀行取引なんかでmove(振込)出来るよ?と思われる方もいるでしょうが、あれは銀行内部の取引をインターネットを介して依頼しているだけで、仕組み自体は疑似moveといったところです。

インターネットは商取引やデータのやり取りに長けていますが、実はmoveが超苦手で、どんどんコピーばかりを作ってしまいます。
なので、著作物の違法コピーや違法ダウンロードは、法律で取り締まるしか対応策がありません。

ブロックチェーンはインターネットに新しく取り付けられたmove機能と言ってもいいかもしれません。
コピーされては困るもの、お金や著作物、株式・債権や契約書、プライバシーに関わるものなど、これまでインターネットでのやり取りをしようと思ったら信頼できる第三者を経由しなくてはいけませんでしたが、これからは直接当人同士がオンラインでやり取りできるようになるというものです。

ちなみに、開発者にとってはブロックチェーンは勉強しがいのあるテーマです。
ブラウザプログラムのソースは超巨大で、Chromiumの場合ソースコードをリポジトリからダウンロードすると数TBにもなりますが、ビットコイン・コアのソースはzipでダウンロードすると8MBしかなく、一つ一つのソースを簡単に見ることが出来ます。
いつか自分もブロックチェーン・プログラムを書いてみたい!