File: blk03381.txt

/ViaBTC/Mined by meno5371/,
7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474nd
7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474nC
FjDOUT:3D9F0A0449D91C3343F0BFEF3EB23AA18B601B147213F9A4559BA64718E8AF0E
IjGREFUND:56077326FC10BAAB6E0230AF2A8E293F37BAF1EF14CD5A0D4F4C1C3FA04A0262
FjDOUT:96BECFE94CA30AD066515B74539F97AF51EABF2945E03DCC80D0A406DF0DA249
7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n
text/html;charset=utf-8
<!doctypehtml><html lang="en"><meta charset="UTF-8"><title>600 000 000 000</title><style>body{background:#f7931a;color:#fff;text-align:center}h1{font-weight:700;margin:0;font-size:666px;line-height:.9;font-family:sans}</style><h1>600 000 000 000</h1>
GjE=:LTC.LTC:ltc1qtwfdwwp3adpwdgyvejj8k4mcrawnqvdr7gtzr6:2497261523:t:30
7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n4%
c/Foundry USA Pool #dropgold/
Bj@=:ETH.ETH:0xa34fc44e304de518b781bfb3eadef606e5fe9cab:204141549:t
Aj?=:ETH.ETH:0x002eF02188CD5Af452662BAB7B0d217C98edc4c8:61461:te:0
Bj@=:BNB.BNB:bnb1z203xu68awz2ucee2ld92redjwh5kk4f7ttga0:260675:te:0
CjA=:BNB.BNB:bnb1277llrkm5luch2ac8p32g06qmwapczffgdy7d6:1135575:te:0
GjE=:BNB.BTCB-1DE:bnb1xnfqwtwzlxp5d6kk4teejkwt6n7kh665a5t86t:156826:te:0
7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474nh
DjB=:ETH.ETH:0xf3BF52129B6aB158bA6d52BB4eD9ba830D837744:23625389:te:0
c/Foundry USA Pool #dropgold/
8j6BERNSTEIN 2.0 REG f039ea5c-096d-4530-8e95-756b5b652d79
FjD=:THOR.RUNE:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n:396898401658
7j5ion:10.QmY1BXnLuPWpG886pjZySeHZvfdF9QY5DYkmkw5PKcWRhc
LjJs:GAIA.ATOM:cosmos1j0gdax9mfqk46msh25wh6l9gdhrqjdmeffw30e:13271266013:ss:0
Bj@=:ETH.ETH:0x4d7d7c6cC1171f52eEE75a4FAB1F45FAe66afdc2:182237:te:0
CjA=:BNB.BNB:bnb1xnfqwtwzlxp5d6kk4teejkwt6n7kh665a5t86t:2639585:te:0
JjH=:BNB.TWT-8C2:bnb1qfl4g38saelylhw6exkcf3ndcrtajy8k8248zk:1681715407:te:0
KjI=:BNB.BUSD-BD1:bnb1rwtf79az0wu49aju63kcaxayq7aql6qt6uq4mv:2310102804:te:0
FjDOUT:F862F6564CB1139A350C0E40300C50281EDBE5DB5292D1D4B715845C044BF004
IjGREFUND:86892234ED3FA22800E2B0461A4A63E11A976120D8618C42A0F444B5ECBD50F8
EjCs:ETH.ETH:0x749885b96c8989C32DF08480E5F064BcAE8C5Fa3:116120048:ss:0
CjA=:BNB.BNB:bnb17svwgfv49na3xz0e2v90aje5naht5y3k0xz23c:1564930:te:0
c/Foundry USA Pool #dropgold/
FjDOUT:55348E9171D3FE3300F4C09C34E021057116D71E8F309288045F0CF3E2D3DC5B
FjDOUT:3DEA8A61D52864F6A2DB10F76B5BD3800276C0263DC542E5CEE0699A5131B53F
FjDOUT:5B05C23B4CBB896A730AB2775BEBB53E74B3C8499FD1AD381083FE2D6B7B676A
FjDOUT:42273D42823F4157B5640FD5177779052747563AEFC8BF880A152060143197E9
CjAs:DOGE.DOGE:DQB6j8npPxMAd5haECgcoHX2t33kwVSKBm:1036183033564:ss:0
SjLPs:ETH.USDT-13D831EC7:0x32EeD2A329794589294C321b76AE7fF49113a1C2:99371668544:ss:0
Bj@=:ETH.ETH:0xA34fc44E304dE518b781Bfb3eadEF606e5Fe9cAB:202490329:t2
7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n
4j2DC-L5:UE664nEDRqD3F9LQMtKVrcqEvtCZwKLSYQyUr3iTMVE=
=j;Stell Dir vor es ist Krieg und keiner  geht hin fuck Putin Q
/ViaBTC/Mined by amosss1/,
KjISWAPTX:0x9da3abe6075f8e967bbb07467408cad6833ce759e7c03eeaf107d7902206e7e5
text/plain;charset=utf-8
Ordinals Puzzle #2 ...
... dropping soon on the timechain.
This is a key piece to solving the puzzle.
Follow @ordinalsindex on Twitter for updates.
:j8=:RUNE:thor1y5mhjjz8pkzsyha06clwse64s2e48w0y4grttj::wr:0
/ViaBTC/Mined by fermakgbmk/,
Bj@=:ETH.ETH:0x5e8f151E9bc6E30bc99c03b5e1D3198DE8d12D54:837634:te:0
Bj@=:BNB.BNB:bnb1hj0trl02xw5jyk9sgwyq2eraa5tzxf27tlvd72:340214:te:0
Bj@=:ETH.ETH:0x1D45997F8f6ea825A48Eb7aB5B9EbE035b86658c:124526:te:0
KjI=:BNB.TWT-8C2:bnb1jnfpv4jc9w7dam2k2436ny57460jcg36wfslsg:22309929123:te:0
LjJ=:BNB.BUSD-BD1:bnb1lp2c45aa6zdqdevtdjy5eg89t4zfw4tjlke8tj:32491936277:te:0
CjA=:BNB.BNB:bnb1hv9czre8fl6p39wah5f6vg3v9rxcd3pkassjez:2898773:te:0
1\ Powered by Luxor Tech \
7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n
IjGREFUND:E74A26F47301A40D04B406B894AD3CC1E5FDD988C2B70B030CE70D66547BE3DB
IjGREFUND:E19F8EF750D74D4456A618E44F81F5EAD1D6F6ED7EC405940C15FF9CA0826602
KjI=:BNB.BUSD-BD1:bnb1yhpptekq6n78d89w50d4d9ywgrle33p7dc0guv:5970795845:te:0
FjD=:LTC.LTC:ltc1qqx9v7r4e6s39u5twr22trf5lxnlt4qxsj9qfwj:169947468:t:30
7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n
SjLPs:ETH.USDC-E3606EB48:0x834bf675dAf52B5D6fD953a57c83e824521E5ab6:44729677632:ss:0
FjDOUT:0822E39D05D2A2EC8B3F7B76C24CB6A745AF54C82D29959E70BC6F1AA52D0FB9
7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n4J
FjDOUT:A9133BC8DB0377D8E62B2CBEC40E11E4FE3CF46FA7E4E727FFA3EFAFE844F5B4
FjDOUT:0B343EF4DB83F7E26A7E99923530DCDFA3A94D6C48EE918AC8D9A6DABAD86C0A
FjDOUT:65C2EEA9B1495C224076CD341CE6F877E456AED4016092CDB09134C5C0A17FB5
FjDOUT:929E96C055817046DF38568F05ED344E1826363DF414D5A79DF7FBC0D38870F9
FjDOUT:BD9B469110DE9DE96318B455976D3F9028FBD2AE527B0B07B5377617B66F4570
FjDOUT:58808E85E050B8E58933F660199022A6A6EDF384AD197D378E6F9EDC5CD470DE
DjB=:BNB.BNB:bnb1zf0k59rcpqr5r40p2acyqvsep07xklde6d5tv5:22297495:te:0
LjJ=:BNB.BUSD-BD1:bnb1lp2c45aa6zdqdevtdjy5eg89t4zfw4tjlke8tj:24531416280:te:0
7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474nc
FjDOUT:F66CAA38058DFE211215945AD96A60148C46D8868D78DAEC42D7FBF51B989FE6
FjDOUT:2004FE2E7DC28F5ABC76CFC86C0227F417E12E6E977277866815E3E461C65758
FjDOUT:D7E7451B4579E1E5F1E2367C124F9D7543AE0070D54EF5FB7F0CDA6FBCC6BF2B
7j5ion:15.QmbK6uUxWaBS8uqh6pd1iXp4ikQN3xa8AuoYrdvjNVDDzL
Bj@=:ETH.ETH:0x422b204F960ea7A70144D4469ECE10EBE3F3f586:367636:te:0
c/Foundry USA Pool #dropgold/
KjISWAPTX:0x1a0276204b506559f45d1be3834b6e84cbbfee2f1b8e98d0712ec038e7d815b3
FjDOUT:6A3C40F228718A34B8F78AF954C1D62803A9ED5577D798279737EACF67598ABA
FjDOUT:D39DDC050E29BD7172B573535E74CFDDA9CDA576843A557E603521A9C3725FF1
Bj@=:BNB.BNB:bnb1cxdmzc5dk8wrk8v5lr9zksmhzfmm0wwa4f90wr:441835:te:0
 2ce981b37a0440f7353880d1546c91ecH0E
c/Foundry USA Pool #dropgold/
7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n[
FjDOUT:E7F8B93686B68EA211F92E2FF95F6E1DEBC24DFC33DA41617BA5B6A34046E94D
FjDOUT:E7255F20E202A6A113F374C435BA2D9DC38E590F9BF22B0A0FB1FCFFE7CA4A94
DjB=:BNB.BNB:bnb1xlrj688ksx7sw2qatvhrkful7t088l0r282wfd:28216438:te:0
KjI=:BNB.BUSD-BD1:bnb1lp2c45aa6zdqdevtdjy5eg89t4zfw4tjlke8tj:5987743462:te:0
text/html;charset=utf-8
  <title>Basic Missile Command HTML Game</title>
  <meta charset="UTF-8">
    background: black;
    align-items: center;
    justify-content: center;
    cursor: crosshair;
    border: 1px solid white;
<canvas width="800" height="550" id="game"></canvas>
const canvas = document.getElementById('game');
const context = canvas.getContext('2d');M
const groundY = 500;  // y position of where the ground starts
const cityWidth = 45;  // how wide a city rect is
const cityHeight = 25;  // how tall a city rect is
const cityY = groundY - cityHeight;  // y position of the city
const siloY = groundY - 30;  // y position of the top of the silo
const missileSize = 4;  // the radius/size of a missile
const missileSpeed = 1;  // how fast a missile moves
const counterMissileSpeed = 15;  // how fast a counter-missile moves
// information about each missile
let counterMissiles = [];
// information about each explosion
let explosions = [];
// how many missiles to spawn at each interval of the level (in this
// case spawn 4 missiles at the start of level 1 and 4 more missiles
// at the next interval of level 1)
const levels = [ [4, 4] ];
let currInterval = 0;
// the x/y position of all cities and if the city is currently alive
  { x: 140, y: cityY, alive: true },
  { x: 220, y: cityY, alive: true },
  { x: 300, y: cityY, aliveM
  { x: 500, y: cityY, alive: true },
  { x: 580, y: cityY, alive: true },
  { x: 660, y: cityY, alive: true }
// the x position of each of the 3 silos
const siloPos = [ 55, canvas.width / 2, 745 ];
// the x/y position of each silo, the number of missiles left, and if
// it is still alive
  { x: siloPos[0], y: siloY, missiles: 10, alive: true },
  { x: siloPos[1], y: siloY, missiles: 10, alive: true },
  { x: siloPos[2], y: siloY, missiles: 10, alive: true }
// the x/y position of eaM
ch missile spawn point. missiles spawn
// directly above each city and silo plus the two edges
const missileSpawns = cities
  .concat([{ x: 0, y: 0 }, { x: canvas.width, y: 0 }])
  .map(pos => ({ x: pos.x, y: 0 }));
// return a random integer between min (inclusive) and max (inclusive)
// @see https://stackoverflow.com/a/1527820/2124254
function randInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
// get the angle between two points
function angleBetweenPoints(source,M
  // atan2 returns the counter-clockwise angle in respect to the
  // x-axis, but the canvas rotation system is based on the y-axis
  // (rotation of 0 = up).
  // so we need to add a quarter rotation to return a
  // counter-clockwise rotation in respect to the y-axis
  return Math.atan2(target.y - source.y, target.x - source.x) + Math.PI / 2;
// distance between two points
function distance(source, target) {
  return Math.hypot(source.x - target.x, source.y - target.y);
// spawn a missile by choM
osing a spawn point and a target.
// a missile can target any city or silo
function spawnMissile() {
  const targets = cities.concat(silos);
  const randSpawn = randInt(0, missileSpawns.length - 1);
  const randTarget = randInt(0, targets.length - 1);
  const start = missileSpawns[randSpawn];
  const target = targets[randTarget];
  const angle = angleBetweenPoints(start, target);
    start,  // where the missile started
    target, // where the missile is going
    pos: { x: start.x, y: start.y M
},  // current position
    alive: true,  // if we should still draw the missile
    // used to update the position every frame
    dx: missileSpeed * Math.sin(angle),
    dy: missileSpeed * -Math.cos(angle)
// start at -2 seconds (time is in milliseconds) to give the player 1
// second before the missiles start
let lastTime = -2000;
function loop(time) {
  requestAnimationFrame(loop);
  context.clearRect(0,0,canvas.width,canvas.height);
  // spawn missiles every interval of 3 seconds (if thM
  if (time - lastTime > 3000 && currInterval < levels[currLevel].length) {
    for (let i = 0; i < levels[currLevel][currInterval]; i++) {
      spawnMissile();
    lastTime = time;
  context.fillStyle = 'blue';
  cities.forEach(city => {
    // center the city on the x position
    context.fillRect(city.x - cityWidth / 2, city.y, cityWidth, cityHeight);
  // draw ground and silos
  context.fillStyle = 'yellow';
  context.moveTo(0, canvas.height);
  context.lineTo(0, groundY);
  // draw each silo hill
  siloPos.forEach(x => {
    context.lineTo(x - 40, groundY);
    context.lineTo(x - 20, siloY);
    context.lineTo(x + 20, siloY);
    context.lineTo(x + 40, groundY);
  context.lineTo(canvas.width, groundY);
  context.lineTo(canvas.width, canvas.height);
  // draw the number of counter-missiles each silo
  context.fillStyle = 'black';
  silos.forEach(silo => {
    // draw missiles in aM
 triangular shape by incrementing how many
    // missiles we can draw per row
    let missilesPerRow = 1;
    let y = silo.y + 5;
    for (let i = 0; i < silo.missiles; i++) {
      context.fillRect(x, y, 4, 10);
      if (++count === missilesPerRow) {
        x = silo.x - 6 * count;
        missilesPerRow++;
  // update and draw missiles
  context.strokeStyle = 'red';
  context.lineWidth = 2;
te color based on time so it "blinks"
  // by dividing by a number and seeing if it's odd or even we can
  // change the speed of the blinking
  context.fillStyle = 'white';
  if (Math.round(time / 2) % 2 === 0) {
    context.fillStyle = 'black';
  missiles.forEach(missile => {
    missile.pos.x += missile.dx;
    missile.pos.y += missile.dy;
    // check if the missile hit an explosion by doing a circle-circle
    // collision check
    explosions.forEach(explosion => {
      const dist = distance(explosion,M
      if (dist < missileSize + explosion.size) {
        missile.alive = false;
    // if missile is close the the target we blow it up
    const dist = distance(missile.pos, missile.target);
    if (dist < missileSpeed) {
      missile.alive = false;
      missile.target.alive = false;
    if (missile.alive) {
      context.beginPath();
      context.moveTo(missile.start.x, missile.start.y);
      context.lineTo(missile.pos.x, missile.pos.y);
      context.stroke();
 center the head of the missile to the x/y position
      context.fillRect(missile.pos.x - missileSize / 2, missile.pos.y - missileSize / 2, missileSize, missileSize);
    // a dead missile spawns an explosion
      explosions.push({
        x: missile.pos.x,
        y: missile.pos.y,
  // update and draw counter missiles
  context.strokeStyle = 'blue';
  context.fillStyle = 'white';
  counterMissiles.forEach(missile => {
   missile.pos.x += missile.dx;
    missile.pos.y += missile.dy;
    // if missile is close the the target we blow it up
    const dist = distance(missile.pos, missile.target);
    if (dist < counterMissileSpeed) {
      missile.alive = false;
      explosions.push({
        x: missile.pos.x,
        y: missile.pos.y,
      context.beginPath();
      context.moveTo(missile.start.x, missile.start.y);
      context.lineTo(missile.pos.x, M
      context.stroke();
      context.fillRect(missile.pos.x - 2, missile.pos.y - 2, 4, 4);
  // update and draw explosions
  explosions.forEach(explosion => {
    explosion.size += 0.35 * explosion.dir;
    // change the direction of the explosion to wane
    if (explosion.size > 30) {
      explosion.dir = -1;
    // remove the explosion
    if (explosion.size <= 0) {
      explosion.alive = false;
      context.fillStyle = 'white';
      if (Math.round(time M
        context.fillStyle = 'blue';
      context.beginPath();
      context.arc(explosion.x, explosion.y, explosion.size, 0, 2 * Math.PI);
      context.fill();
  // remove dead missiles, explosions, cities, and silos
  missiles = missiles.filter(missile => missile.alive);
  counterMissiles = counterMissiles.filter(missile => missile.alive);
  explosions = explosions.filter(explosion => explosion.alive);
  cities = cities.filter(city => city.alive);
  silos = silos.filter(siM
// listen to mouse events to fire counter-missiles
canvas.addEventListener('click', e => {
  // get the x/y position of the mouse pointer by subtracting the x/y
  // position of the canvas element from the x/y position of the
  const x = e.clientX - e.target.offsetLeft;
  const y = e.clientY - e.target.offsetTop;
  // determine which silo is closest to the pointer and fire a
  // counter-missile from it
  let launchSilo = null;
  let siloDistance = Infinity;  // start at the largM
  silos.forEach(silo => {
    const dist = distance({ x, y }, silo);
    if (dist < siloDistance && silo.missiles) {
      siloDistance = dist;
      launchSilo = silo;
    const start = { x: launchSilo.x, y: launchSilo.y };
    const target = { x, y };
    const angle = angleBetweenPoints(start, target);
    launchSilo.missiles--;
    counterMissiles.push({
      pos: { x: launchSilo.x, y: launchSilo. y},
      dx: counterMissileSpeed * Math.siL
      dy: counterMissileSpeed * -Math.cos(angle),
requestAnimationFrame(loop);
c/Foundry USA Pool #dropgold/
7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n
SjLPs:ETH.FOX-D808EE52D:0x1B9bB0A0A55f30edAA6A03D55190C0Ad72CacaE5:241474682209:ss:0
FjDOUT:9B16F1F08FF0A18A233ADC125C2F22992BCB915282ED375718A0CF1E3CAB0DB0
FjDOUT:23E09844249581E34AFC600F74676C74035825C0A15212AE97CC82B986BE7F11
FjDOUT:C3405794A3B01D9868B96CFBA26E2C1E9950BAAADABEE89BDE6FEACBF01B39A4
IjGREFUND:D3FE02B230362B719DF221E068738DD82244F463594A249384FCAE5568160EFE
LjJ=:BNB.BUSD-BD1:bnb16hjkt6tfdwsmy7saqdymh3qy7c24uerep79mts:14478139561:te:0
text/html;charset=utf-8
  <title>Basic Doodle Jump HTML Game</title>
  <meta charset="UTF-8">
    align-items: center;
    justify-content: center;
    border: 1px solid black;
<canvas width="375" height="667" id="game"></canvas>
const canvas = document.getElementById('game');
const context = canvas.getContext('2d');
// width and height of each platform and where pM
const platformWidth = 65;
const platformHeight = 20;
const platformStart = canvas.height - 50;
const gravity = 0.33;
const bounceVelocity = -12.5;
// minimum and maximum vertical space between each platform
let minPlatformSpace = 15;
let maxPlatformSpace = 20;
// information about each platform. the first platform starts in the
// bottom middle of the screen
  x: canvas.width / 2 - platformWidth / 2,
// get a random numM
ber between the min (inclusive) and max (exclusive)
function random(min, max) {
  return Math.random() * (max - min) + min;
// fill the initial screen with platforms
let y = platformStart;
  // the next platform can be placed above the previous one with a space
  // somewhere between the min and max space
  y -= platformHeight + random(minPlatformSpace, maxPlatformSpace);
  // a platform can be placed anywhere 25px from the left edge of the canvas
  // and 25px from the right edge of the canvas M
(taking into account platform
  // however the first few platforms cannot be placed in the center so
  // that the player will bounce up and down without going up the screen
  // until they are ready to move
    x = random(25, canvas.width - 25 - platformWidth);
    y > canvas.height / 2 &&
    x > canvas.width / 2 - platformWidth * 1.5 &&
    x < canvas.width / 2 + platformWidth / 2
  platforms.push({ x, y });
// the doodle jumper
  x: canvas.width / 2 - 20,
  y: platformStart - 60,
// keep track of player direction and actions
let keydown = false;
let prevDoodleY = doodle.y;
  requestAnimationFrame(loop);
  context.clearRect(0,0,canvas.width,canvas.height);
  // apply gravity to doodle
  doodle.dy += gravity;
  // if doodle reaches the middle of the screen, move the platforms down
  // instead of doodle up to make it look like doodle is going M
  if (doodle.y < canvas.height / 2 && doodle.dy < 0) {
    platforms.forEach(function(platform) {
      platform.y += -doodle.dy;
    // add more platforms to the top of the screen as doodle moves up
    while (platforms[platforms.length - 1].y > 0) {
      platforms.push({
        x: random(25, canvas.width - 25 - platformWidth),
        y: platforms[platforms.length - 1].y - (platformHeight + random(minPlatformSpace, maxPlatformSpace))
      // add a bit to the min/max platform space as the M
      minPlatformSpace += 0.5;
      maxPlatformSpace += 0.5;
      // cap max space
      maxPlatformSpace = Math.min(maxPlatformSpace, canvas.height / 2);
    doodle.y += doodle.dy;
  // only apply drag to horizontal movement if key is not pressed
    if (playerDir < 0) {
      doodle.dx += drag;
      // don't let dx go above 0
      if (doodle.dx > 0) {
        doodle.dx = 0;
        playerDir = 0;
    else if (playerDir > 0) {
      if (doodle.dx < 0) {
        doodle.dx = 0;
        playerDir = 0;
  doodle.x += doodle.dx;
  // make doodle wrap the screen
  if (doodle.x + doodle.width < 0) {
    doodle.x = canvas.width;
  else if (doodle.x > canvas.width) {
    doodle.x = -doodle.width;
  context.fillStyle = 'green';
  platforms.forEach(function(platform) {
    context.fillRect(platform.x, platform.y, platformWidth, platformHeight);
    // make doodle jump if it collides wiM
th a platform from above
      // doodle is falling
      doodle.dy > 0 &&
      // doodle was previous above the platform
      prevDoodleY + doodle.height <= platform.y &&
      // doodle collides with platform
      // (Axis Aligned Bounding Box [AABB] collision check)
      doodle.x < platform.x + platformWidth &&
      doodle.x + doodle.width > platform.x &&
      doodle.y < platform.y + platformHeight &&
      doodle.y + doodle.height > platform.y
      // reset doodle position so it's on tM
      doodle.y = platform.y - doodle.height;
      doodle.dy = bounceVelocity;
  context.fillStyle = 'yellow';
  context.fillRect(doodle.x, doodle.y, doodle.width, doodle.height);
  prevDoodleY = doodle.y;
  // remove any platforms that have gone offscreen
  platforms = platforms.filter(function(platform) {
    return platform.y < canvas.height;
// listen to keyboard events to move doodle
document.addEventListener('keydown', function(e) {
  // left arrow keMa
  if (e.which === 37) {
  // right arrow key
  else if (e.which === 39) {
document.addEventListener('keyup', function(e) {
requestAnimationFrame(loop);

%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X'
c/Foundry USA Pool #dropgold/
Bj@=:ETH.ETH:0x7c5C5cf47141389d3D9fA90dd5250Cb19d5b2064:551599:te:0
EjCs:RUNE:thor1k8dd70sddrwcq3r5xsfvy3veqjylxm4qhs2z6k:35497349738:ss:0
IjGREFUND:ED77D2FB6F9CA99819AE78C4BBD01D4D38861B4E4B2691046A914C9261B42B5A
IjGREFUND:F36D2F52AC6FF5BEF334485B58DEA40CACA20440947BBC30E4817C6C4E44F48D
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
  <title>Bitcoin Face</title>
  <g style="isolation:isolate"><rect id="Background-5" width="1025" height="1025" style="fill:#f8f3c1" /><g id="Body-8"><rect x="161.21" y="452.66" width="687.79" height="552.16" style="fill:#ba3d11" /><rect x="309.41" y="451.15" width="382.27" height="553.67" style="fill:#542f5f" /><rect x="500.36" y="451.15" width="194.64" height="553.67" style="fill:#172027" /></g><g id="Head-11"><path d="M164.72,490.82C164.72,683.66,3M
17,840,504.85,840S845,683.66,845,490.82" transform="translate(0.28)" style="fill:#f5b659" /><path d="M186,490.82c0,180.79,142.76,327.34,318.87,327.34S823.71,671.61,823.71,490.82" transform="translate(0.28)" style="fill:#ecdea0" /><path d="M228.5,490.82c0,156.68,123.72,283.7,276.35,283.7s276.34-127,276.34-283.7" transform="translate(0.28)" style="fill:#e18d27" /><polygon points="506.79 774.52 503.45 774.52 503.45 510.93 781.47 510.93 781.47 514.36 506.79 514.36 506.79 774.52" style="fill:#ecdea0" /><polygon points="M
695.33 688.51 497.93 508.38 760.74 598.31 759.68 601.56 512.31 516.91 697.55 685.94 695.33 688.51" style="fill:#ecdea0" /><polygon points="609.89 753.4 503.6 513.35 506.64 511.94 612.93 751.98 609.89 753.4" style="fill:#ecdea0" /><polygon points="506.79 774.52 503.45 774.52 503.45 514.36 228.77 514.36 228.77 510.93 506.79 510.93 506.79 774.52" style="fill:#ecdea0" /><polygon points="314.91 688.51 312.69 685.94 497.93 516.91 250.56 601.56 249.5 598.31 512.31 508.38 314.91 688.51" style="fill:#ecdea0" /><polygon poinM
ts="400.35 753.4 397.31 751.98 503.6 511.94 506.64 513.35 400.35 753.4" style="fill:#ecdea0" /><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M664.28,601.65A12.63,12.63,0,1,1,676.58,589,12.47,12.47,0,0,1,664.28,601.65Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,664.28,579.83Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M643,579.83a12.63,12.63,0,1,1,12.3-12.63A12.49,12.49,0,0,1,643,579.83ZM643,558a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,643,5M
58Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M738.68,579.83c-12.64,0-22.93-10.56-22.93-23.54s10.29-23.54,22.93-23.54,22.92,10.56,22.92,23.54S751.32,579.83,738.68,579.83Zm0-43.65c-10.8,0-19.59,9-19.59,20.11s8.79,20.11,19.59,20.11,19.58-9,19.58-20.11S749.48,536.18,738.68,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M685.53,536.18a12.63,12.63,0,1,1,12.3-12.63A12.47,12.47,0,0M
,1,685.53,536.18Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.08,9.08,0,0,0,685.53,514.36Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M483.59,732.59c-12.64,0-22.93-10.56-22.93-23.54s10.29-23.54,22.93-23.54,22.93,10.56,22.93,23.54S496.23,732.59,483.59,732.59Zm0-43.65c-10.8,0-19.59,9-19.59,20.11s8.79,20.11,19.59,20.11,19.58-9,19.58-20.11S494.39,688.94,483.59,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:oveM
rlay"><path d="M451.7,688.94A12.63,12.63,0,1,1,464,676.32,12.48,12.48,0,0,1,451.7,688.94Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,451.7,667.12Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M387.93,601.65A12.63,12.63,0,1,1,400.23,589,12.48,12.48,0,0,1,387.93,601.65Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,387.93,579.83Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M324.16,558M
a12.63,12.63,0,1,1,12.3-12.62A12.47,12.47,0,0,1,324.16,558Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,324.16,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M260.38,558a12.63,12.63,0,1,1,12.3-12.62A12.47,12.47,0,0,1,260.38,558Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,260.38,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M292.27,645.3c-12.64,0-22.93-10.56-22.93-23.54sM
10.29-23.54,22.93-23.54,22.93,10.56,22.93,23.54S304.91,645.3,292.27,645.3Zm0-43.65c-10.8,0-19.59,9-19.59,20.11s8.79,20.11,19.59,20.11,19.59-9,19.59-20.11S303.07,601.65,292.27,601.65Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M324.16,667.12a12.63,12.63,0,1,1,12.3-12.63A12.48,12.48,0,0,1,324.16,667.12Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,324.16,645.3Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-M
mode:overlay"><path d="M600.5,667.12a12.63,12.63,0,1,1,12.3-12.63A12.48,12.48,0,0,1,600.5,667.12Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.08,9.08,0,0,0,600.5,645.3Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M664.28,710.76a12.63,12.63,0,1,1,12.3-12.62A12.47,12.47,0,0,1,664.28,710.76Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,664.28,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M611M
.13,732.59c-12.64,0-22.92-10.56-22.92-23.54s10.28-23.54,22.92-23.54,22.93,10.56,22.93,23.54S623.77,732.59,611.13,732.59Zm0-43.65c-10.8,0-19.58,9-19.58,20.11s8.78,20.11,19.58,20.11,19.59-9,19.59-20.11S621.94,688.94,611.13,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M706.79,645.3a12.63,12.63,0,1,1,12.3-12.63A12.49,12.49,0,0,1,706.79,645.3Zm0-21.83a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,706.79,623.47Z" transform="translate(0.28)" style="fill:#aeaM
4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M473,579.83a12.63,12.63,0,1,1,12.3-12.63A12.49,12.49,0,0,1,473,579.83ZM473,558a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,473,558Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M430.45,536.18a12.63,12.63,0,1,1,12.29-12.63A12.48,12.48,0,0,1,430.45,536.18Zm0-21.82a9.2,9.2,0,1,0,8.95,9.19A9.09,9.09,0,0,0,430.45,514.36Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.M
52;mix-blend-mode:overlay"><path d="M579.25,558a12.63,12.63,0,1,1,12.3-12.62A12.48,12.48,0,0,1,579.25,558Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,579.25,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M558,601.65A12.63,12.63,0,1,1,570.29,589,12.48,12.48,0,0,1,558,601.65Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,558,579.83Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M4M
94.22,649.82a17.15,17.15,0,1,1,16.7-17.15A17,17,0,0,1,494.22,649.82Zm0-30.87a13.72,13.72,0,1,0,13.36,13.72A13.56,13.56,0,0,0,494.22,619Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M536.73,710.76A12.63,12.63,0,1,1,549,698.14,12.48,12.48,0,0,1,536.73,710.76Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,536.73,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M558,758.93a17.15,17.15M
,0,1,1,16.7-17.15A16.95,16.95,0,0,1,558,758.93Zm0-30.86a13.72,13.72,0,1,0,13.36,13.71A13.55,13.55,0,0,0,558,728.07Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M366.67,710.76A12.63,12.63,0,1,1,379,698.14,12.48,12.48,0,0,1,366.67,710.76Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,366.67,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><rect x="164.72" y="490.82" width="680.24" height="21.82" transform="translate(1009.97 1003.46) rotaM
te(-180)" style="fill:#aea4b3;opacity:0.52;mix-blend-mode:overlay" /><path d="M845.81,507.17C845.81,314.33,693.53,158,505.69,158S165.57,314.33,165.57,507.17" transform="translate(0.28)" style="fill:#f5b659" /><path d="M824.55,507.17c0-180.79-142.76-327.35-318.86-327.35S186.82,326.38,186.82,507.17" transform="translate(0.28)" style="fill:#ecdea0" /><path d="M782,507.17c0-156.69-123.73-283.7-276.35-283.7s-276.35,127-276.35,283.7" transform="translate(0.28)" style="fill:#e18d27" /><polygon points="504.29 223.47 507.63M
 223.47 507.63 487.06 229.62 487.06 229.62 483.63 504.29 483.63 504.29 223.47" style="fill:#ecdea0" /><polygon points="315.76 309.48 513.15 489.61 250.35 399.68 251.4 396.43 498.77 481.07 313.54 312.04 315.76 309.48" style="fill:#ecdea0" /><polygon points="401.19 244.58 507.49 484.63 504.44 486.05 398.15 246 401.19 244.58" style="fill:#ecdea0" /><polygon points="504.29 223.47 507.63 223.47 507.63 483.63 782.31 483.63 782.31 487.06 504.29 487.06 504.29 223.47" style="fill:#ecdea0" /><polygon points="696.17 309.48 69M
8.39 312.04 513.15 481.07 760.53 396.43 761.58 399.68 498.77 489.61 696.17 309.48" style="fill:#ecdea0" /><polygon points="610.73 244.58 613.77 246 507.49 486.05 504.44 484.63 610.73 244.58" style="fill:#ecdea0" /><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M346.26,396.34A12.63,12.63,0,1,1,334,409,12.48,12.48,0,0,1,346.26,396.34Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,346.26,418.16Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M367.M
51,418.16a12.63,12.63,0,1,1-12.29,12.63A12.48,12.48,0,0,1,367.51,418.16Zm0,21.82a9.2,9.2,0,1,0-8.95-9.19A9.08,9.08,0,0,0,367.51,440Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M271.85,418.16c12.65,0,22.93,10.56,22.93,23.54s-10.28,23.54-22.93,23.54-22.92-10.56-22.92-23.54S259.21,418.16,271.85,418.16Zm0,43.65c10.81,0,19.59-9,19.59-20.11s-8.78-20.11-19.59-20.11-19.58,9-19.58,20.11S261.05,461.81,271.85,461.81Z" transform="translate(0.28)" style="fill:M
#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M325,461.81a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,325,461.81Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,325,483.63Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M527,265.4c12.64,0,22.92,10.56,22.92,23.54S539.59,312.48,527,312.48,504,301.92,504,288.94,514.3,265.4,527,265.4Zm0,43.65c10.8,0,19.58-9,19.58-20.11s-8.78-20.11-19.58-20.11-19.59,9-19.59,20.11S516.15,309.05,527,M
309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M558.83,309.05a12.63,12.63,0,1,1-12.3,12.62A12.47,12.47,0,0,1,558.83,309.05Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,558.83,330.87Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M622.61,396.34A12.63,12.63,0,1,1,610.31,409,12.47,12.47,0,0,1,622.61,396.34Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,622.61,418.16Z" transform="tranM
slate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M686.38,440a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,686.38,440Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,686.38,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M750.15,440a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,750.15,440Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,750.15,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" />M
</g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M718.26,352.69c12.65,0,22.93,10.56,22.93,23.54s-10.28,23.54-22.93,23.54-22.92-10.56-22.92-23.54S705.62,352.69,718.26,352.69Zm0,43.65c10.81,0,19.59-9,19.59-20.11s-8.78-20.11-19.59-20.11-19.58,9-19.58,20.11S707.46,396.34,718.26,396.34Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M686.38,330.87a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,686.38,330.87Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09M
,0,0,0,686.38,352.69Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M410,330.87a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,410,330.87Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,410,352.69Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M346.26,287.22A12.63,12.63,0,1,1,334,299.85,12.49,12.49,0,0,1,346.26,287.22Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,346.26,309.05Z" transform=M
"translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M399.4,265.4c12.64,0,22.93,10.56,22.93,23.54S412,312.48,399.4,312.48s-22.93-10.56-22.93-23.54S386.76,265.4,399.4,265.4Zm0,43.65c10.8,0,19.59-9,19.59-20.11s-8.79-20.11-19.59-20.11-19.59,9-19.59,20.11S388.6,309.05,399.4,309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M303.74,352.69a12.63,12.63,0,1,1-12.3,12.63A12.48,12.48,0,0,1,303.74,352.69Zm0,21M
.82a9.2,9.2,0,1,0-9-9.19A9.09,9.09,0,0,0,303.74,374.51Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M537.58,418.16a12.63,12.63,0,1,1-12.3,12.63A12.47,12.47,0,0,1,537.58,418.16Zm0,21.82a9.2,9.2,0,1,0-9-9.19A9.08,9.08,0,0,0,537.58,440Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M580.09,461.81a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,580.09,461.81Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9M
.09,9.09,0,0,0,580.09,483.63Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M431.29,440A12.63,12.63,0,1,1,419,452.61,12.49,12.49,0,0,1,431.29,440Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,431.29,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M452.54,396.34A12.63,12.63,0,1,1,440.25,409,12.47,12.47,0,0,1,452.54,396.34Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,452.54,418.16Z"M
 transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M516.32,348.17a17.15,17.15,0,1,1-16.7,17.15A16.95,16.95,0,0,1,516.32,348.17Zm0,30.86A13.72,13.72,0,1,0,503,365.32,13.55,13.55,0,0,0,516.32,379Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M473.8,287.22a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,473.8,287.22Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,473.8,309.05Z" transform="translaM
te(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M452.54,239.06a17.15,17.15,0,1,1-16.7,17.14A17,17,0,0,1,452.54,239.06Zm0,30.86a13.72,13.72,0,1,0-13.36-13.72A13.56,13.56,0,0,0,452.54,269.92Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M643.86,287.22a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,643.86,287.22Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,643.86,309.05Z" transform="translate(0.28)" style="fM
ill:#aea4b3" /></g><rect x="165.84" y="485.34" width="680.24" height="21.82" style="fill:#aea4b3;opacity:0.52;mix-blend-mode:overlay" /></g><g id="Face-Accessory-1"><circle cx="516.52" cy="942.46" r="62.02" style="fill:#f96020" /><circle cx="516.52" cy="942.46" r="54.28" style="fill:#d74816" /><rect x="487.05" y="879.79" width="35.52" height="96.79" transform="translate(214.46 1954.46) rotate(-135)" style="fill:#ffa520" /><circle cx="510.95" cy="942.07" r="66.36" style="fill:#f96020" /><circle cx="510.94" cy="942.0M
7" r="58.08" style="fill:#d74816" /><g id="_04CbBX.tif" data-name="04CbBX.tif"><path d="M499.36,875.6h5.23a5.28,5.28,0,0,0,1.13.08c1.51.13,3,.28,4.52.52a52.15,52.15,0,0,1,10.43,2.72,53.73,53.73,0,0,1,32.17,33.55,52.41,52.41,0,0,1,2.21,10c.15,1.22.26,2.45.31,3.67a1.63,1.63,0,0,0,.08.82v4.31a1.52,1.52,0,0,0-.08.82,46.85,46.85,0,0,1-.52,5.14,52,52,0,0,1-5.39,16.51,53.57,53.57,0,0,1-43.89,28.75,50.62,50.62,0,0,1-8-.07,52.07,52.07,0,0,1-10.21-1.83,53.13,53.13,0,0,1-29.73-21.46,53,53,0,0,1-9.05-25.57,17,17,0,0,0-.16-1.88M
c0-.61,0-1.22,0-1.83,0-1.1,0-2.2,0-3.3a.77.77,0,0,0,.07-.4,53.09,53.09,0,0,1,5.25-20.37,53.67,53.67,0,0,1,35.15-28.6,54.71,54.71,0,0,1,9.34-1.48A8.57,8.57,0,0,0,499.36,875.6Zm-42,53.5a44.61,44.61,0,1,0,44.59-44.62A44.59,44.59,0,0,0,457.33,929.1Z" transform="translate(9.03 13)" style="fill:#ff9317" /><path d="M515.36,927.51a21.52,21.52,0,0,1,5.48,1.69,11.83,11.83,0,0,1,5.27,4.7,12,12,0,0,1,1.52,4.64,17.76,17.76,0,0,1-.27,6.21,10.87,10.87,0,0,1-6.8,8,25.21,25.21,0,0,1-5.77,1.53c-1.34.21-2.68.35-4,.44-.47,0-.47,0-.47.M
51v7.94c0,.5,0,.51-.51.51H504c-.56,0-.53,0-.53-.53v-7.68c0-.64.05-.57-.58-.57h-5.8c-.46,0-.46,0-.47.48v7.73c0,.59,0,.57-.54.57h-5.74c-.55,0-.55,0-.55-.57,0-2.51,0-5,0-7.53,0-.14,0-.27,0-.41s-.06-.29-.26-.27H476.43c-.24,0-.34-.06-.34-.31,0-1.9,0-3.79,0-5.69,0-.27.11-.35.36-.35,1.18,0,2.36,0,3.54,0a10.18,10.18,0,0,0,2.13-.27,1.92,1.92,0,0,0,1.55-1.57,11.89,11.89,0,0,0,.29-2.85V921.52c0-2.47,0-4.95,0-7.43a13.56,13.56,0,0,0-.27-2.69,2,2,0,0,0-1.71-1.68,13.39,13.39,0,0,0-2.75-.24h-2.66c-.47,0-.47,0-.47-.49,0-1.76,0-3.51M
,0-5.27,0-.6-.06-.56.54-.57h12.55c.57,0,.57,0,.58-.54v-7.89c0-.2.09-.28.28-.26h6c.61,0,.56-.06.56.54,0,2.51,0,5,0,7.53,0,.7-.05.62.64.62H503c.46,0,.46,0,.47-.49v-7.94c0-.2.07-.28.27-.26h6c.59,0,.57-.09.57.58v7.84c0,.39,0,.39.39.42a32.44,32.44,0,0,1,6.11,1,17.64,17.64,0,0,1,3.2,1.2,9.17,9.17,0,0,1,5,6.06,14.22,14.22,0,0,1,.12,7.24,10.31,10.31,0,0,1-4.17,6,18.31,18.31,0,0,1-5.34,2.53C515.55,927.36,515.43,927.34,515.36,927.51Zm-18.76,12.4c0,2.77,0,5.53,0,8.3,0,.44,0,.45.44.45h3a44,44,0,0,0,6.39-.31,15,15,0,0,0,3.66-.9M
3,6.31,6.31,0,0,0,3.8-4.16,12,12,0,0,0,.26-6,6.32,6.32,0,0,0-2.8-4.24A9.11,9.11,0,0,0,508.7,932a23.93,23.93,0,0,0-4.61-.65c-2.39-.14-4.78,0-7.17-.08-.26,0-.34.1-.32.33s0,.24,0,.36Zm0-22.76v4.25c0,1.05,0,2.09,0,3.13,0,.25,0,.37.34.37a68.58,68.58,0,0,0,7.72-.23,14.47,14.47,0,0,0,3.67-.89,5.57,5.57,0,0,0,3.45-4A11.75,11.75,0,0,0,512,915a5.49,5.49,0,0,0-3.83-4.58,18.25,18.25,0,0,0-5-.88c-2-.1-4,0-6,0h-.21c-.17,0-.25.08-.24.25s0,.27,0,.41Z" transform="translate(9.03 13)" style="fill:#ff9317" /></g><circle cx="459.8" cy=M
"882.7" r="13.97" style="fill:#ff9317" /><circle cx="438.84" cy="861.74" r="13.97" style="fill:#ff9317" /><circle cx="417.89" cy="840.79" r="13.97" style="fill:#ff9317" /><circle cx="396.93" cy="819.83" r="13.97" style="fill:#ff9317" /><circle cx="564.57" cy="882.7" r="13.97" style="fill:#f96020" /><circle cx="585.53" cy="861.74" r="13.97" style="fill:#f96020" /><circle cx="606.48" cy="840.79" r="13.97" style="fill:#f96020" /><circle cx="627.44" cy="819.83" r="13.97" style="fill:#f96020" /></g><g id="Earrings-1"><cM
ircle cx="141.68" cy="475.44" r="61.24" style="fill:#f26227" /><circle cx="141.68" cy="475.44" r="53.6" style="fill:#d74b27" /><g><path d="M134.36,420.84h4.82a6.06,6.06,0,0,0,1,.07c1.4.12,2.79.26,4.18.48A48.47,48.47,0,0,1,154,423.9a49.56,49.56,0,0,1,29.69,31,47.44,47.44,0,0,1,2,9.2c.13,1.13.24,2.25.28,3.39a1.48,1.48,0,0,0,.07.75v4a1.37,1.37,0,0,0-.07.75,42.22,42.22,0,0,1-.48,4.75,47.88,47.88,0,0,1-5,15.23,49.3,49.3,0,0,1-47.87,26.48,47.84,47.84,0,0,1-9.43-1.7,49.07,49.07,0,0,1-27.44-19.8,49.09,49.09,0,0,1-8.35-23.6M
,12.49,12.49,0,0,0-.15-1.73c0-.57,0-1.13,0-1.69,0-1,0-2,0-3.05a.57.57,0,0,0,.07-.36,47.07,47.07,0,0,1,.76-6.38,48.23,48.23,0,0,1,4.09-12.43,49.32,49.32,0,0,1,41.06-27.75A9.08,9.08,0,0,0,134.36,420.84ZM95.57,470.21A41.17,41.17,0,1,0,136.72,429,41.12,41.12,0,0,0,95.57,470.21Z" transform="translate(4.96 5.26)" style="fill:#f79421" /><path d="M149.13,468.75a19.6,19.6,0,0,1,5.05,1.56,10.88,10.88,0,0,1,4.87,4.33,11.08,11.08,0,0,1,1.4,4.28,16.24,16.24,0,0,1-.25,5.73,10.06,10.06,0,0,1-6.28,7.4,23.28,23.28,0,0,1-5.33,1.41c-M
1.23.19-2.46.33-3.71.4-.43,0-.44,0-.44.48v7.33c0,.46,0,.46-.47.46h-5.35c-.52,0-.49,0-.49-.48v-7.09c0-.59.05-.53-.54-.53h-5.34c-.43,0-.43,0-.43.44,0,2.38,0,4.76,0,7.14,0,.55.05.52-.5.52H126c-.5,0-.51,0-.51-.52v-7.33c0-.17-.06-.27-.24-.25s-.26,0-.38,0H113.19c-.21,0-.31,0-.31-.29q0-2.62,0-5.25c0-.25.1-.32.34-.31h3.26a9.33,9.33,0,0,0,2-.26,1.76,1.76,0,0,0,1.43-1.45,11,11,0,0,0,.26-2.63q0-10.31,0-20.61c0-2.29,0-4.57,0-6.86a11.52,11.52,0,0,0-.24-2.48,1.88,1.88,0,0,0-1.58-1.55,12.77,12.77,0,0,0-2.54-.23c-.82,0-1.64,0-2.46M
,0-.43,0-.43,0-.43-.45v-4.87c0-.56,0-.52.5-.52H125c.52,0,.52,0,.52-.51v-6.9c0-.12,0-.25,0-.38s.09-.25.26-.24h5.54c.56,0,.51-.06.51.5,0,2.32,0,4.63,0,6.95,0,.65,0,.58.58.58h5.3c.43,0,.43,0,.43-.46v-7c0-.11,0-.22,0-.33s.07-.26.25-.25h5.54c.55,0,.52-.08.52.54V446c0,.36,0,.35.37.38a30.6,30.6,0,0,1,5.63.91,16.49,16.49,0,0,1,3,1.11,8.5,8.5,0,0,1,4.65,5.6,13.15,13.15,0,0,1,.11,6.68,9.54,9.54,0,0,1-3.85,5.55,17,17,0,0,1-4.93,2.34C149.3,468.61,149.19,468.58,149.13,468.75Zm-17.31,11.44c0,2.55,0,5.1,0,7.66,0,.41,0,.41.41.41H1M
35a40.14,40.14,0,0,0,5.9-.28,14.22,14.22,0,0,0,3.38-.86,5.83,5.83,0,0,0,3.5-3.84,11,11,0,0,0,.24-5.51,5.78,5.78,0,0,0-2.58-3.91,8.31,8.31,0,0,0-2.49-1,21.66,21.66,0,0,0-4.25-.6c-2.2-.13-4.41,0-6.62-.07-.23,0-.31.09-.29.3s0,.22,0,.33Q131.81,476.5,131.82,480.19Zm0-21v3.92q0,1.44,0,2.88c0,.23.05.35.31.34a62.82,62.82,0,0,0,7.13-.2,13.67,13.67,0,0,0,3.39-.83,5.13,5.13,0,0,0,3.18-3.67,10.6,10.6,0,0,0,.15-4.45,5,5,0,0,0-3.52-4.22,16.7,16.7,0,0,0-4.64-.82c-1.86-.09-3.72,0-5.58,0H132a.2.2,0,0,0-.22.23v.38Q131.81,456,131.81,M
459.19Z" transform="translate(4.96 5.26)" style="fill:#f79421" /></g><circle cx="897.68" cy="478.42" r="61.24" style="fill:#f26227" /><circle cx="897.68" cy="478.42" r="53.6" style="fill:#d74b27" /><g><path d="M890.36,423.81h4.82a5.2,5.2,0,0,0,1,.07c1.4.12,2.79.27,4.18.48a48.5,48.5,0,0,1,9.62,2.52,49.52,49.52,0,0,1,29.69,31,47.6,47.6,0,0,1,2,9.2c.13,1.13.24,2.26.28,3.39a1.52,1.52,0,0,0,.07.76v4a1.4,1.4,0,0,0-.07.76,41.81,41.81,0,0,1-.48,4.74,47.88,47.88,0,0,1-5,15.23,49.44,49.44,0,0,1-40.51,26.54,47.59,47.59,0,0,1-M
7.36-.06,48.57,48.57,0,0,1-9.43-1.69,49.12,49.12,0,0,1-27.44-19.8,49.17,49.17,0,0,1-8.35-23.6,12.63,12.63,0,0,0-.15-1.74c0-.56,0-1.12,0-1.69,0-1,0-2,0-3a.62.62,0,0,0,.07-.37,47.11,47.11,0,0,1,.76-6.37,48.1,48.1,0,0,1,4.09-12.43,49.49,49.49,0,0,1,32.44-26.4,50.07,50.07,0,0,1,8.62-1.36A7.27,7.27,0,0,0,890.36,423.81Zm-38.79,49.37A41.17,41.17,0,1,0,892.72,432,41.14,41.14,0,0,0,851.57,473.18Z" transform="translate(4.96 5.26)" style="fill:#f79421" /><path d="M905.13,471.72a19.6,19.6,0,0,1,5,1.56,10.85,10.85,0,0,1,4.87,4.M
34,11,11,0,0,1,1.4,4.28,16.25,16.25,0,0,1-.25,5.73,10,10,0,0,1-6.28,7.39,23.28,23.28,0,0,1-5.33,1.41c-1.23.19-2.46.33-3.71.41-.43,0-.44,0-.44.47v7.33c0,.46,0,.47-.47.47h-5.35c-.52,0-.49,0-.49-.49v-7.09c0-.59,0-.53-.54-.53h-5.34c-.43,0-.43,0-.43.45,0,2.38,0,4.76,0,7.14,0,.54.05.52-.5.52H882c-.5,0-.51,0-.51-.53v-7.33c0-.17-.06-.26-.24-.25H869.19c-.21,0-.31-.05-.31-.28q0-2.62,0-5.25c0-.26.1-.32.34-.32h3.26a9.33,9.33,0,0,0,2-.26,1.76,1.76,0,0,0,1.43-1.45,11,11,0,0,0,.26-2.62q0-10.32,0-20.62c0-2.28,0-4.57,0-6.85a11.55,1M
1.55,0,0,0-.24-2.49,1.88,1.88,0,0,0-1.58-1.55,12.77,12.77,0,0,0-2.54-.23h-2.46c-.43,0-.43,0-.43-.45v-4.87c0-.55,0-.52.5-.52H881c.52,0,.52,0,.52-.5v-6.91c0-.12,0-.25,0-.37s.09-.26.26-.25h5.54c.56,0,.51-.05.51.5,0,2.32,0,4.64,0,6.95,0,.65-.05.58.58.58h5.3c.43,0,.43,0,.43-.45v-7c0-.11,0-.22,0-.33s.07-.26.25-.25h5.54c.55,0,.52-.07.52.55V449c0,.36,0,.36.37.38a30.62,30.62,0,0,1,5.63.92,16.45,16.45,0,0,1,3,1.1,8.5,8.5,0,0,1,4.65,5.6,13.19,13.19,0,0,1,.11,6.69,9.49,9.49,0,0,1-3.85,5.54,16.79,16.79,0,0,1-4.93,2.34C905.3,471M
.58,905.19,471.56,905.13,471.72Zm-17.31,11.44c0,2.55,0,5.11,0,7.66,0,.41,0,.41.41.41H891a40.14,40.14,0,0,0,5.9-.29,14.2,14.2,0,0,0,3.38-.85,5.85,5.85,0,0,0,3.5-3.85,11,11,0,0,0,.24-5.51,5.78,5.78,0,0,0-2.58-3.91,8.31,8.31,0,0,0-2.49-1,23.27,23.27,0,0,0-4.25-.6c-2.2-.13-4.41,0-6.62-.07-.23,0-.31.09-.29.31s0,.22,0,.33C887.81,478.25,887.81,480.7,887.82,483.16Zm0-21v3.92c0,1,0,1.92,0,2.89,0,.22.05.34.31.33a62.82,62.82,0,0,0,7.13-.2,13.67,13.67,0,0,0,3.39-.83,5.11,5.11,0,0,0,3.18-3.66,10.65,10.65,0,0,0,.15-4.46,5,5,0,0,M
0-3.52-4.22,16.68,16.68,0,0,0-4.64-.81c-1.86-.1-3.72,0-5.58-.05H888c-.15,0-.23.07-.22.23v.38Q887.8,458.92,887.81,462.16Z" transform="translate(4.96 5.26)" style="fill:#f79421" /></g></g><g id="Ears-6"><path d="M305.64,230v64A22.69,22.69,0,0,1,283,316.61H262.15a97.69,97.69,0,0,0-37.57,7.51h0a87.1,87.1,0,0,1-112.14-43L108,271.88a86.58,86.58,0,0,1,44.06-116.81h0A86.61,86.61,0,0,1,208.5,151l89.26,23.81H187.46a39.39,39.39,0,0,0-39.39,39.39h0a39.4,39.4,0,0,0,39.39,39.39h4.85A39.42,39.42,0,0,0,212.58,248l40.76-24.46a42.8,M
42.8,0,0,1,52.3,6.44Z" transform="translate(0.34)" style="fill:#542f5f" /><path d="M722.64,230.16v64a22.68,22.68,0,0,0,22.68,22.68h20.81a97.64,97.64,0,0,1,37.56,7.51h0a87.11,87.11,0,0,0,112.15-42.95l4.42-9.29a86.58,86.58,0,0,0-44.07-116.81h0a86.58,86.58,0,0,0-56.41-4.07L730.51,175h110.3A39.39,39.39,0,0,1,880.2,214.4h0a39.38,39.38,0,0,1-39.39,39.39H836a39.42,39.42,0,0,1-20.27-5.61l-40.77-24.46a42.8,42.8,0,0,0-52.29,6.44Z" transform="translate(0.34)" style="fill:#111c0d" /></g><g id="Mouth-1"><path d="M504.57,545.25HM
236.78c-25.78,0-40.38,29.55-24.72,50l35.83,46.85a64.57,64.57,0,0,0,51.31,25.36H504.57" transform="translate(0.28 0)" style="fill:#883a62" /><path d="M483,545.25H775.4c25.78,0,40.38,29.55,24.72,50l-35.83,46.85A64.57,64.57,0,0,1,713,667.49H483" transform="translate(0.28 0)" style="fill:#883a62" /><rect x="267.55" y="570.35" width="474.59" height="64.72" style="fill:#542f5f" /><rect x="505.9" y="556.65" width="247.44" height="37.12" style="fill:#172027" /><path d="M712.79,556.31h0a15.47,15.47,0,0,1,15.47,15.47v21.65a0M
,0,0,0,1,0,0H697.33a0,0,0,0,1,0,0V571.78A15.47,15.47,0,0,1,712.79,556.31Z" style="fill:#f489ae" /><rect x="505.56" y="624.36" width="204.14" height="24.74" style="fill:#172027" /><rect x="654.03" y="624.36" width="30.93" height="24.74" style="fill:#f489ae" /><path d="M691.21,594.11v55.34h37.12A52.45,52.45,0,0,0,709.23,609Z" transform="translate(0.28 0)" style="fill:#f489ae" /><path d="M740.7,575.21v61.86l13.35-11.13a66,66,0,0,0,23.77-50.73h0a18.56,18.56,0,0,0-18.56-18.56h0A18.56,18.56,0,0,0,740.7,575.21Z" transformM
="translate(0.28 0)" style="fill:#f489ae" /><rect x="257.84" y="556.31" width="247.44" height="37.12" transform="translate(763.4 1149.74) rotate(-180)" style="fill:#172027" /><path d="M282.93,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556a0,0,0,0,1,0,0Z" transform="translate(597.06 1149.05) rotate(-180)" style="fill:#f9c1c0" /><rect x="301.49" y="624.01" width="204.14" height="24.74" transform="translate(807.39 1272.77) rotate(-180)" style="fill:#172027" /><rect x="3M
26.23" y="624.01" width="30.93" height="24.74" transform="translate(683.67 1272.77) rotate(-180)" style="fill:#f9c1c0" /><path d="M319.7,593.77V649.1H282.58a52.47,52.47,0,0,1,19.1-40.48Z" transform="translate(0.28 0)" style="fill:#f9c1c0" /><path d="M270.21,574.87v61.86L256.86,625.6a66,66,0,0,1-23.77-50.73h0a18.56,18.56,0,0,1,18.56-18.56h0A18.56,18.56,0,0,1,270.21,574.87Z" transform="translate(0.28 0)" style="fill:#f9c1c0" /><path d="M329.67,556H360.6a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,M
0,0,1-15.47-15.47V556A0,0,0,0,1,329.67,556Z" transform="translate(690.54 1149.05) rotate(-180)" style="fill:#f9c1c0" /><rect x="372.97" y="624.01" width="30.93" height="24.74" transform="translate(777.15 1272.77) rotate(-180)" style="fill:#f9c1c0" /><path d="M375.14,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556a0,0,0,0,1,0,0Z" transform="translate(781.48 1149.05) rotate(-180)" style="fill:#f9c1c0" /><rect x="418.44" y="624.01" width="30.93" height="24.74" transform=M
"translate(868.09 1272.77) rotate(-180)" style="fill:#f9c1c0" /><path d="M423.15,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556A0,0,0,0,1,423.15,556Z" transform="translate(877.5 1149.05) rotate(-180)" style="fill:#f9c1c0" /><rect x="466.45" y="624.01" width="30.93" height="24.74" transform="translate(964.1 1272.77) rotate(-180)" style="fill:#f9c1c0" /><path d="M473.64,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556A0,0,M
0,0,1,473.64,556Z" transform="translate(978.48 1149.05) rotate(-180)" style="fill:#f9c1c0" /><rect x="513.19" y="624.7" width="30.93" height="24.74" transform="translate(1057.58 1274.15) rotate(-180)" style="fill:#f489ae" /><rect x="559.93" y="624.7" width="30.93" height="24.74" transform="translate(1151.06 1274.15) rotate(-180)" style="fill:#f489ae" /><rect x="605.4" y="624.7" width="30.93" height="24.74" transform="translate(1242 1274.15) rotate(-180)" style="fill:#f489ae" /><rect x="653.41" y="624.7" width="30.9M
3" height="24.74" transform="translate(1338.02 1274.15) rotate(-180)" style="fill:#f489ae" /><path d="M509.43,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556a0,0,0,0,1,0,0Z" transform="translate(1050.07 1149.05) rotate(-180)" style="fill:#f489ae" /><path d="M554.9,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556A0,0,0,0,1,554.9,556Z" transform="translate(1141.02 1149.05) rotate(-180)" style="fill:#f489ae" /><path d="M602.M
91,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556A0,0,0,0,1,602.91,556Z" transform="translate(1237.03 1149.05) rotate(-180)" style="fill:#f489ae" /><path d="M653.41556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556a0,0,0,0,1,0,0Z" transform="translate(1338.02 1149.05) rotate(-180)" style="fill:#f489ae" /></g><g id="Eyebrows-1">undefined</g><g id="Glasses-4">undefined</g><g id="Eyes-3"><rect x="568.09" y="366.84" width="201M
.78" height="108.65" rx="51.21" transform="translate(-105 257.04) rotate(-19.91)" style="fill:#3d2f39" /><circle cx="645.54" cy="433.1" r="46.56" style="fill:#eb6447" /><rect x="639.33" y="390.29" width="15.52" height="77.61" rx="7.32" transform="translate(-109.01 250.06) rotate(-19.91)" style="fill:#3d2f39" /><rect x="241.28" y="368.37" width="201.78" height="108.65" rx="51.21" transform="translate(518.36 940.65) rotate(-160.09)" style="fill:#80191b" /><circle cx="362.51" cy="434.62" r="46.56" style="fill:#eb6447"M
 /><rect x="356.3" y="391.82" width="15.52" height="77.61" rx="7.32" transform="translate(558.13 963.49) rotate(-160.09)" style="fill:#3d2f39" /></g><g id="Nose-2"><circle cx="478.44" cy="516.44" r="9.88" style="fill:#303636" /><path d="M503,519.73h-3.65a24.52,24.52,0,0,0-49,0H446.7a28.17,28.17,0,0,1,56.34,0Z" transform="translate(0.28 0)" style="fill:#efe1da" /><circle cx="535.24" cy="515.88" r="9.88" style="fill:#303636" /><path d="M510.08,519.17h3.66a24.52,24.52,0,0,1,49,0h3.66a28.18,28.18,0,0,0-56.35,0Z" transfMG
orm="translate(0.28 0)" style="fill:#efe1da" /></g><g id="Hat-5"><path d="M704.39,126H507.13l12.77,10.22A138.53,138.53,0,0,0,704.39,126Z" transform="translate(0.28)" style="fill:#ffa520" /><path d="M703.55,126H506.29l12.77-10.22A138.53,138.53,0,0,1,703.55,126Z" transform="translate(0.28)" style="fill:#b98d22" /></g></g></svg>h!
c/Foundry USA Pool #dropgold/
SjLPs:ETH.USDC-E3606EB48:0x834bf675dAf52B5D6fD953a57c83e824521E5ab6:21579472667:ss:0
JjH=:BNB.BUSD-BD1:bnb1kcx0e9l8a0d0ckvfmd7pf4l4y9qspk9ye285pp:224477524:te:0
c/Foundry USA Pool #dropgold/
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
<svg viewBox="0 0 1287 1287" xmlns="http://www.w3.org/2000/svg"><g transform="translate(664.886 1117.481)"><path d="M165.818 104.54c-11.243 45.096-56.917 72.54-102.017 61.294-45.082-11.243-72.527-56.92-61.279-102.012 11.238-45.1 56.912-72.547 102-61.305 45.097 11.243 72.54 56.925 61.296 102.023z" fill="#F7931A"/><path d="M121.27 72.185c1.676-11.201-6.852-17.223-18.514-21.24l3.783-15.172-9.236-2.302-3.682 14.773a386.353 386.353 0 0 0-7.4-1.741l3.709-14.87-9.23-2.303-3.786 15.168c-2.01-.458-3.983-.91-5.898-1.386l.01-M
.047-12.736-3.18-2.457 9.864s6.852 1.57 6.708 1.667c3.74.934 4.416 3.41 4.303 5.372L56.48 98.353c-.458 1.137-1.618 2.841-4.233 2.194.093.134-6.713-1.676-6.713-1.676l-4.585 10.573 12.02 2.996c2.235.56 4.426 1.147 6.583 1.699l-3.822 15.347 9.226 2.301 3.785-15.183a351.26 351.26 0 0 0 7.36 1.91l-3.772 15.112 9.236 2.302 3.822-15.318c15.749 2.98 27.592 1.778 32.577-12.466 4.016-11.47-.2-18.085-8.487-22.399 6.035-1.392 10.58-5.361 11.793-13.56zm-21.102 29.59c-2.854 11.47-22.165 5.27-28.426 3.715l5.072-20.332c6.26 1.563 M
26.337 4.656 23.354 16.618zm2.857-29.756c-2.604 10.433-18.677 5.132-23.89 3.833l4.597-18.44c5.214 1.299 22.005 3.724 19.293 14.607z" fill="#FFF"/></g><path d="M203.138 300.399v-69.033H79.068v-47.384h100.653v-69.034H79.068V69.607h124.07V.573L9.638.982v299.417h193.5zm105.171 0V.165h-69.43v300.234h69.43zm110.512 0v-174.83l98.598 174.83H592.6V.573h-69.43v175.239L424.984 1.39h-75.591v299.009h69.43zm-288.81 328.056c15.611-.273 30.332-3.472 44.163-9.6 13.832-6.127 25.882-14.365 36.153-24.713 10.27-10.348 18.35-22.466 24.2M
39-36.355 5.888-13.888 8.696-28.593 8.422-44.116v-185.45h-69.43V514.08c0 6.263-1.095 12.118-3.286 17.564-2.191 5.447-5.341 10.213-9.45 14.297-4.108 4.085-8.9 7.353-14.378 9.804-5.478 2.45-11.23 3.676-17.255 3.676-12.325 0-23.006-4.22-32.044-12.663-9.038-8.986-13.558-19.47-13.558-31.453V328.22h-69.43v187.494c.275 15.522 3.356 30.091 9.245 43.707 5.888 13.616 14.31 25.87 25.265 36.764 10.682 10.348 22.87 18.313 36.564 23.896 13.694 5.582 28.21 8.374 43.547 8.374h1.233zm223.9 0v-174.83l98.598 174.83h75.18V328.629H458.M
26v175.239l-98.187-174.422h-75.592v299.009h69.43zm271.966 0c20.541-1.09 39.576-5.65 57.105-13.684 17.528-8.034 32.797-18.723 45.807-32.066 13.01-13.344 23.212-28.798 30.606-46.363 7.395-17.565 11.093-36.423 11.093-56.575 0-20.424-3.766-39.623-11.298-57.596s-17.802-33.768-30.812-47.384c-13.01-13.616-28.347-24.645-46.012-33.087-17.666-8.442-36.495-13.207-56.489-14.297v-.408h-69.43v301.46h69.43zm0-69.442V396.437c10.682 1.09 20.541 4.017 29.58 8.782 9.038 4.766 16.98 10.893 23.827 18.382 6.848 7.489 12.188 16.067 16.02M
3 25.734 3.834 9.668 5.751 19.812 5.751 30.432 0 10.348-1.917 20.152-5.751 29.41-3.835 9.26-9.107 17.361-15.817 24.306-6.71 6.944-14.653 12.663-23.828 17.156-9.175 4.493-19.103 7.284-29.785 8.374zM240.934 956.51v-69.033h-124.48l126.123-232.835-225.954-.408v69.442h109.69L.19 956.51h240.744zm179.12 0 34.509-150.321 39.85 150.321h68.608l89.149-301.868h-72.716l-48.888 166.252-44.37-166.252H418l-38.206 167.07-55.873-167.07h-73.127L351.035 956.51h69.019zm293.74 0 12.325-36.354h94.9l11.914 36.354h73.127l-97.366-299.826h-6M
5.732L640.256 956.51h73.538zm85.04-105.388h-48.888l25.06-72.71 23.829 72.71zM996.032 956.51V781.68l98.598 174.83h75.181V656.684h-69.43v175.24L1002.194 657.5h-75.592v299.01h69.43zm-755.097 328.056v-69.033h-124.48l126.123-232.835-225.954-.409v69.442h109.69L.19 1284.566h240.744zm112.155 0V984.332h-69.43v300.234h69.43zm168.85.409c7.942 0 15.953-.613 24.033-1.838 8.08-1.226 15.953-3.2 23.622-5.923v7.352h69.84V1116.68H509.203v69.033h60.391v15.114c-7.943 5.72-16.57 9.804-25.882 12.255-9.312 2.45-18.693 3.336-28.142 2.655-M
9.449-.68-18.624-2.996-27.525-6.944-8.901-3.949-16.912-9.463-24.033-16.544-7.943-7.897-13.968-16.884-18.076-26.96-4.109-10.076-6.163-20.288-6.163-30.636 0-10.348 2.054-20.56 6.163-30.636 4.108-10.076 10.133-19.062 18.076-26.96 7.943-7.897 16.98-13.82 27.114-17.769 10.134-3.948 20.405-5.923 30.812-5.923 10.408 0 20.679 1.975 30.812 5.923 10.134 3.949 19.172 9.872 27.115 17.77l49.299-49.019c-14.79-14.705-31.497-25.734-50.121-33.087-18.624-7.352-37.659-11.029-57.105-11.029-19.445 0-38.48 3.677-57.104 11.03-18.625 7.35M
2-35.331 18.381-50.121 33.086-14.79 14.706-25.814 31.317-33.072 49.835-7.258 18.518-10.887 37.444-10.887 56.78 0 19.334 3.63 38.192 10.887 56.574 7.258 18.382 18.282 34.925 33.072 49.63 14.79 14.706 31.496 25.735 50.12 33.088 18.625 7.352 37.66 11.029 57.105 11.029z"/></svg>
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
779NSsssu777Sssu777Ssssssu7Su7Sssu7
5sE4SE4SE4SE4SE4SE4SE4SE4S
YYYYYYY[++++++++++xuP.
MjK=:BNB.BUSD-BD1:bnb1z2kyzy0aapxpu59wxd7gm6pgdwaetl2nxgfk7y:585682793830:t:30
4j2DC-L5:xvy+CDsUGhuDNIBjfx9Ga0czIayXMNvLfgIC1yT+ki0=
Bj@=:BNB.BNB:bnb1gjrduftzncf80xp947ehmpp3ldse9gf4ajcmr8:330621:te:0
CjA=:BNB.BNB:bnb1lp2c45aa6zdqdevtdjy5eg89t4zfw4tjlke8tj:2894238:te:0
MjK=:BNB.BUSD-BD1:bnb1nxx047muxr57qhrgxh3q8nnalfzzjcn2zp9j3q:184696061056:te:0
FjDOUT:94458AD0A3EE8ED38F11CE5C794E4F3A438B4EB144D031DF292B9DC5994F7080
6j4ion:2.QmYY7sjXV23CpAdCatTFRbQABJq6X6iheEAWd6oGaqUx2E
IjG=:THOR.RUNE:thor104ep0k5pjeame62zuahfjxlqfh323uenx08k0e:5055581576:t:30HO
CjA=:BNB.BNB:bnb1r5atp9qhf3ye7ant26ujurv0xamklx0t2seyud:3157523:te:0
FjDOUT:8375162D8627AFD60BA3D79A966F9BC36C0AA80B903689463D1637A4C3F60C19
FjDOUT:ED27E52140C8D63CB28E0C6A552E861F42A921AD210111953B356596B81A1C60
GjE=:BNB.BTCB-1DE:bnb14aaca79pjxk739wvgshj4xpw2lshht6mp35as3:136669:te:0
FjDOUT:71C04475D3E773ABF5D8DC3345DC3966E2EB0D2581C94743FB42115F277E7309
FjDOUT:DB0A69453C456B94E445304B0868D0F9979ACA77184BA612EFACCADB95B9AC5F
FjDOUT:88639A08BFDEFA527B9D5C87E35FDEE0860DBCF56E1030F92A925C235F8FC917
FjDOUT:C0A54E71C08D00B9025287FA51D7F6A32A5A33EA25BAD1BF6D2CDA60F2FA31CF
Bj@=:ETH.ETH:0x2dc0601A0465f924CDFdf3Bd8f0d9bD13bc461c0:632131:te:0
FjDOUT:ACBA1F4263EB038B4B84A44EA8E0C4CB7A7BCF6AB4D5C1F8F25F01154833BF87
CjA=:ETH.ETH:0x57b558E7068FA48Abe1d5CE686d0204CAa8b5d0d:4599891:te:0
c/Foundry USA Pool #dropgold/
EjC=:ETH.ETH:0x209053265e81db305151f9ec4f3b1ddb6cd6a109:140246830:t:30
FjDOUT:7143897A0C0056E702E689D73B87CE32FA01E5DC6115BF45FE3CB12B4385D066
FjDOUT:72FA33EC1B338AECBE3B217B285119CD5ACF09795C80AB02100F1F3B866CB068
9j7+:BTC/BTC::bc1q3f787hr38pmal87yxtpq8tng09q60ljjqqd759:0
IjGREFUND:F70F4F2F24AC5D7AF736C6C5DAE30679EA42ED811269EA451B01EB9E83C3C219
%j#Voglio Apple Watch Ultra in titanio
DjB=:ETH.ETH:0xf1Aa42bcD5e0381f765D223bE36B951F2705fcbc:44511314:te:0
FjDOUT:7DE1612E88A9000A24ACBB3220ECD0FF8FF46B54F701EBA6F7F73248254A81EA
IjGREFUND:82181A63D05B693E7C3835536FE95FB2B57FC85FD3E85EB8DFCC0EC7E6D42D46
9j7+:BTC/BTC::bc1q3f787hr38pmal87yxtpq8tng09q60ljjqqd759:0
FjDOUT:1DEB996AFF135B4A76AB5066A9CB16650E2410B00BA00AEB85C36CF6DCDD26D7
FjDOUT:35D76ED1C5A0DC4896C094632211ACF685DC111DC30910E003BC732D72CE0A4F
c/Foundry USA Pool #dropgold/
IjGREFUND:779A24D38D48B64BFD74D927B5BF0EAABC83C31A00AC3ED12BB44E539C0229C4
FjDOUT:2E77AB172488F5E91560654EC822E4FBEC601AA6C50417C476B6310E607BFD15
FjDOUT:C6C381E5A06848D05B0ED6FB50F1B90B56A16C8A7E2CB4FD514752CC18DB7906
FjDOUT:5B999C63D1BB7CA12DADC47C10D0AA1AEFC4A963EE57674C6DEC8A84F46E35BD
FjDOUT:B11EBD2B75450CA79BAC6BCA4136A6BD6AB3CD44DAA564FCFEA8FEB06BB97DEE
FjDOUT:441928A2460851F1F22C07CE6BD4693784B6E7F2158FBAF6E04F231AE21822B1
FjDOUT:A16F258CDEFA98E20DB8C4BC138EFC8B1F29460DD5D3130ED889D8BBC67D5447
FjDOUT:D914E0688C17425B824AB8753D36B2973312357B201ECAE49FBE36FF4899DFB4
Aj?=:BNB.BNB:bnb1d5ewawcrmclzce38uacd6r9xmsjd28yyuykwnn:14775:te:0
CjA=:BNB.BNB:bnb18d42fg2uvw4vd66p664x9l7ftcv3y3hqw9rgf7:7696858:te:0
Copyright Apple Inc., 2018
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz

c/Foundry USA Pool #dropgold/
6j4ion:2.Qmbsm8fynSG5bKoGa79f3x2zbQymNYnciGT5kY2KxMmJ7od
FjDOUT:16447E99F86951EA191448A60136FF7B7B86500CEEDD933B7A4A39FF94C47C93
EjCs:BCH.BCH:qqqmr73y9gp73c80dctmhtrn648x580y7g50zay2d2:704464616:ss:0
FjDOUT:0326D38CA45714706B3F22569647A6775061CED89CC4D98E419FC21ABFF4473E
FjDOUT:353DE24E89CC21EDA1F7E5E221FDED0BDF550ACD83950BE861B0D8277C171F01
HjF=:BNB.ETH-1C9:bnb1qzgyz7rxf2qgyk3v4rxsh64aetw25s45u2l7h3:13873802:te:0
*j(Inscriptions are an attack on OP_RETURNs
Created with GIMPd.e
c/Foundry USA Pool #dropgold/
FjD=:THOR.RUNE:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n:494191304313
EjCs:RUNE:thor17c0svnvxklf3yydzdfdml22djamzhkkcp9lph3:52535926643:ss:0
TUUUUUUUUUUUUUUUUUUUUUUUUUUUU
text/plain;charset=utf-8
# Merged Python Files
logger = logging.getLogger(__name__)
from counterpartylib.lib import config
from counterpartylib.lib import script
def address_scriptpubkey(address):
        bech32 = bitcoin.bech32.CBech32Data(address)
        return b''.join([b'\x00\x14', bech32.to_bytes()])
    except Exception as e:
        bs58 = bitcoin.base58.decode(address)[1:-4]
        return b''.join([b'\x76\xa9\x14', bs58, b'\x88\xac'])
    Converts a base58 bitcoin address into a 21 byte bytes object
    from .util import enabled # Here to account for test mock changes
    if enabled('segwit_support'):
            bech32 = bitcoin.bech32.CBech32Data(address)
            witver = (0x80 + bech32.witver).to_bytes(1, byteorder='big') # mark the first byte for segwit
            witprog = bech32.to_bytes()
            if len(witprog) > 20:
                raise Exception('p2wsh still not supported for sending')
       return b''.join([witver, witprog])
        except Exception as ne:
                script.validate(address) #This will check if the address is valid
                short_address_bytes = bitcoin.base58.decode(address)[:-4]
                return short_address_bytes
            except bitcoin.base58.InvalidBase58Error as e:
                raise e
            except Exception as e:
                raise Exception(('The address {} is not a valid bitcoin address ({})').format(address,'testnet' M
if config.TESTNET or config.REGTEST else 'mainnet'))
            short_address_bytes = bitcoin.base58.decode(address)[:-4]
            return short_address_bytes
        except bitcoin.base58.InvalidBase58Error as e:
# retuns both the message type id and the remainder of the message data
def unpack(short_address_bytes):
    Converts a 21 byte prefix and public key hash into a full base58 bitcoin address
    from .util import enabled # Here to account for tM
    if enabled('segwit_support') and short_address_bytes[0] >= 0x80 and short_address_bytes[0] <= 0x8F:
        # we have a segwit address here
        witver = short_address_bytes[0] - 0x80
        witprog = short_address_bytes[1:]
        return str(bitcoin.bech32.CBech32Data.from_bytes(witver, witprog))
        check = bitcoin.core.Hash(short_address_bytes)[0:4]
        return bitcoin.base58.encode(short_address_bytes + check)
se connections are read
only, so SQL injection attacks can
logger = logging.getLogger(__name__)
from logging import handlers as logging_handlers
from flask_httpauth import HTTPBasicAuth
from jsonrpc import dispatcher
from jsonrpc.exceptioM
ns import JSONRPCDispatchException
from xmltodict import unparse as serialize_to_xml
from counterpartylib.lib import config
from counterpartylib.lib import exceptions
from counterpartylib.lib import util
from counterpartylib.lib import check
from counterpartylib.lib import backend
from counterpartylib.lib import database
from counterpartylib.lib import transaction
from counterpartylib.lib import blocks
from counterpartylib.lib import script
from counterpartylib.lib import message_type
rtylib.lib.messages import send
from counterpartylib.lib.messages.versions import enhanced_send
from counterpartylib.lib.messages import order
from counterpartylib.lib.messages import btcpay
from counterpartylib.lib.messages import issuance
from counterpartylib.lib.messages import broadcast
from counterpartylib.lib.messages import bet
from counterpartylib.lib.messages import dividend
from counterpartylib.lib.messages import burn
from counterpartylib.lib.messages import destroy
from counterpartylib.lib.messages impoM
from counterpartylib.lib.messages import rps
from counterpartylib.lib.messages import rpsresolve
from counterpartylib.lib.messages import sweep
from counterpartylib.lib.messages import dispenser
API_TABLES = ['assets', 'balances', 'credits', 'debits', 'bets', 'bet_matches',
              'broadcasts', 'btcpays', 'burns', 'cancels', 'destructions',
              'dividends', 'issuances', 'orders', 'order_matches', 'sends',
              'bet_expirations', 'order_expirations', 'bet_match_expirations',
          'order_match_expirations', 'bet_match_resolutions', 'rps',
              'rpsresolves', 'rps_matches', 'rps_expirations', 'rps_match_expirations',
              'mempool', 'sweeps', 'dispensers', 'dispenses','transactions']
API_TRANSACTIONS = ['bet', 'broadcast', 'btcpay', 'burn', 'cancel', 'destroy',
                    'dividend', 'issuance', 'order', 'send',
                    'rps', 'rpsresolve', 'sweep', 'dispenser']
COMMONS_ARGS = ['encoding', 'fee_per_kb', 'regular_dust_size',
ultisig_dust_size', 'op_return_value', 'pubkey',
                'allow_unconfirmed_inputs', 'fee', 'fee_provided',
                'estimate_fee_per_kb', 'estimate_fee_per_kb_nblocks', 'estimate_fee_per_kb_conf_target', 'estimate_fee_per_kb_mode',
                'unspent_tx_hash', 'custom_inputs', 'dust_return_pubkey', 'disable_utxo_locks', 'extended_tx_info',
                'p2sh_source_multisig_pubkeys', 'p2sh_source_multisig_pubkeys_required', 'p2sh_pretx_txid']
API_MAX_LOG_SIZE = 10 * 1024 * 1024 #max log sM
ize of 20 MB before rotation (make configurable later)
API_MAX_LOG_COUNT = 10
JSON_RPC_ERROR_API_COMPOSE = -32001 #code to use for error composing transaction result
current_api_status_code = None #is updated by the APIStatusPoller
current_api_status_response_json = None #is updated by the APIStatusPoller
class APIError(Exception):
class BackendError(Exception):
def check_backend_state():
    """Checks blocktime of last block to see if {} Core is running behind.""".format(config.BTC_NAME)
block_count = backend.getblockcount()
    block_hash = backend.getblockhash(block_count)
    cblock = backend.getblock(block_hash)
    time_behind = time.time() - cblock.nTime   # TODO: Block times are not very reliable.
    if time_behind > 60 * 60 * 2:   # Two hours.
        raise BackendError('Bitcoind is running about {} hours behind.'.format(round(time_behind / 3600)))
    # check backend index
    blocks_behind = backend.getindexblocksbehind()
    if blocks_behind > 5:
        raise BackendError('Indexd is rM
unning {} blocks behind.'.format(blocks_behind))
    logger.debug('Backend state check passed.')
class DatabaseError(Exception):
def check_database_state(db, blockcount):
    """Checks {} database to see if is caught up with backend.""".format(config.XCP_NAME)
    if util.CURRENT_BLOCK_INDEX + 1 < blockcount:
        raise DatabaseError('{} database is behind backend.'.format(config.XCP_NAME))
    logger.debug('Database state check passed.')
# TODO: ALL queries EVERYWHERE should be done withM
def db_query(db, statement, bindings=(), callback=None, **callback_args):
    """Allow direct access to the database in a parametrized manner."""
    cursor = db.cursor()
    forbidden_words = ['pragma', 'attach', 'database', 'begin', 'transaction']
    for word in forbidden_words:
        #This will find if the forbidden word is in the statement as a whole word. For example, "transactions" will be allowed because the "s" at the end
        if re.search(r"\b"+word+"\b", statement.lowM
            raise APIError("Forbidden word in query: '{}'.".format(word))
    if hasattr(callback, '__call__'):
        cursor.execute(statement, bindings)
        for row in cursor:
            callback(row, **callback_args)
        results = None
        results = list(cursor.execute(statement, bindings))
def get_rows(db, table, filters=None, filterop='AND', order_by=None, order_dir=None, start_block=None, end_block=None,
              status=None, limit=10M
00, offset=0, show_expired=True):
    """SELECT * FROM wrapper. Filters results based on a filter data structure (as used by the API)."""
    if filters == None:
        filters = []
    def value_to_marker(value):
        # if value is an array place holder is (?,?,?,..)
        if isinstance(value, list):
            return '''({})'''.format(','.join(['?' for e in range(0, len(value))]))
            return '''?'''
    # TODO: Document that op can be anything that SQLite3 accepts.
 or table.lower() not in API_TABLES:
        raise APIError('Unknown table')
    if filterop and filterop.upper() not in ['OR', 'AND']:
        raise APIError('Invalid filter operator (OR, AND)')
    if order_dir and order_dir.upper() not in ['ASC', 'DESC']:
        raise APIError('Invalid order direction (ASC, DESC)')
    if not isinstance(limit, int):
        raise APIError('Invalid limit')
    elif config.API_LIMIT_ROWS != 0 and limit > config.API_LIMIT_ROWS:
        raise APIError('Limit should be lower or equaM
l to %i' % config.API_LIMIT_ROWS)
    elif config.API_LIMIT_ROWS != 0 and limit == 0:
        raise APIError('Limit should be greater than 0')
    if not isinstance(offset, int):
        raise APIError('Invalid offset')
    # TODO: accept an object:  {'field1':'ASC', 'field2': 'DESC'}
    if order_by and not re.compile('^[a-z0-9_]+$').match(order_by):
        raise APIError('Invalid order_by, must be a field name')
    if isinstance(filters, dict): #single filter entry, convert to a one entry list
    elif not isinstance(filters, list):
        filters = []
    # TODO: Document this! (Each filter can be an ordered list.)
    new_filters = []
    for filter_ in filters:
        if type(filter_) in (list, tuple) and len(filter_) in [3, 4]:
            new_filter = {'field': filter_[0], 'op': filter_[1], 'value':  filter_[2]}
            if len(filter_) == 4:
                new_filter['case_sensitive'] = filter_[3]
            new_filters.append(new_filter)
        elif type(filter_) == dict:
         new_filters.append(filter_)
            raise APIError('Unknown filter type')
    filters = new_filters
    # validate filter(s)
    for filter_ in filters:
        for field in ['field', 'op', 'value']: #should have all fields
            if field not in filter_:
                raise APIError("A specified filter is missing the '%s' field" % field)
        if not isinstance(filter_['value'], (str, int, float, list)):
            raise APIError("Invalid value for the field '%s'" % filter_['fM
        if isinstance(filter_['value'], list) and filter_['op'].upper() not in ['IN', 'NOT IN']:
            raise APIError("Invalid value for the field '%s'" % filter_['field'])
        if filter_['op'].upper() not in ['=', '==', '!=', '>', '<', '>=', '<=', 'IN', 'LIKE', 'NOT IN', 'NOT LIKE']:
            raise APIError("Invalid operator for the field '%s'" % filter_['field'])
        if 'case_sensitive' in filter_ and not isinstance(filter_['case_sensitive'], bool):
            raise APIError("case_sensitM
ive must be a boolean")
    # special case for memo and memo_hex field searches
    if table == 'sends':
        adjust_get_sends_memo_filters(filters)
    statement = '''SELECT * FROM {}'''.format(table)
    for filter_ in filters:
        case_sensitive = False if 'case_sensitive' not in filter_ else filter_['case_sensitive']
        if filter_['op'] == 'LIKE' and case_sensitive == False:
            filter_['field'] = '''UPPER({})'''.format(filter_M
            filter_['value'] = filter_['value'].upper()
        marker = value_to_marker(filter_['value'])
        conditions.append('''{} {} {}'''.format(filter_['field'], filter_['op'], marker))
        if isinstance(filter_['value'], list):
            bindings += filter_['value']
            bindings.append(filter_['value'])
    more_conditions = []
    if table not in ['balances', 'order_matches', 'bet_matches']:
        if start_block != None:
            more_conditM
ions.append('''block_index >= ?''')
            bindings.append(start_block)
        if end_block != None:
            more_conditions.append('''block_index <= ?''')
            bindings.append(end_block)
    elif table in ['order_matches', 'bet_matches']:
        if start_block != None:
            more_conditions.append('''tx0_block_index >= ?''')
            bindings.append(start_block)
        if end_block != None:
            more_conditions.append('''tx1_block_index <= ?''')
            bindings.append(end_blM
    if isinstance(status, list) and len(status) > 0:
        more_conditions.append('''status IN {}'''.format(value_to_marker(status)))
        bindings += status
    elif isinstance(status, str) and status != '':
        more_conditions.append('''status == ?''')
        bindings.append(status)
    # legacy filters
    if not show_expired and table == 'orders':
        #Ignore BTC orders one block early.
        expire_index = util.CURRENT_BLOCK_INDEX + 1
        more_conditions.append('''((giveM
_asset == ? AND expire_index > ?) OR give_asset != ?)''')
        bindings += [config.BTC, expire_index, config.BTC]
    if (len(conditions) + len(more_conditions)) > 0:
        statement += ''' WHERE'''
        all_conditions = []
        if len(conditions) > 0:
            all_conditions.append('''({})'''.format(''' {} '''.format(filterop.upper()).join(conditions)))
        if len(more_conditions) > 0:
            all_conditions.append('''({})'''.format(''' AND '''.join(more_conditions)))
        statement += ''M
' {}'''.format(''' AND '''.join(all_conditions))
    if order_by != None:
        statement += ''' ORDER BY {}'''.format(order_by)
        if order_dir != None:
            statement += ''' {}'''.format(order_dir.upper())
    if limit and limit > 0:
        statement += ''' LIMIT {}'''.format(limit)
            statement += ''' OFFSET {}'''.format(offset)
    query_result = db_query(db, statement, tuple(bindings))
    if table == 'balances':
        return adjusM
t_get_balances_results(query_result, db)
    if table == 'destructions':
        return adjust_get_destructions_results(query_result)
    if table == 'sends':
        # for sends, handle the memo field properly
        return adjust_get_sends_results(query_result)
    if table == 'transactions':
        # for transactions, handle the data field properly
        return adjust_get_transactions_results(query_result)
    return query_result
def adjust_get_balances_results(query_result, db):
    for balances_row in list(query_result):
        asset = balances_row['asset']
        if not asset in assets:
            assets[asset] = util.is_divisible(db, asset)
        balances_row['divisible'] = assets[asset]
        filtered_results.append(balances_row)
    return filtered_results
def adjust_get_destructions_results(query_result):
    filtered_results = []
    for destruction_row in list(query_result):
        if type(destruction_row['tag']) == bytes:
estruction_row['tag'] = destruction_row['tag'].decode('utf-8', 'ignore')
        filtered_results.append(destruction_row)
    return filtered_results
def adjust_get_sends_memo_filters(filters):
    """Convert memo to a byte string.  If memo_hex is supplied, attempt to decode it and use that instead."""
    for filter_ in filters:
        if filter_['field'] == 'memo':
            filter_['value'] = bytes(filter_['value'], 'utf-8')
        if filter_['field'] == 'memo_hex':
            # search the indexed memo fM
ield with a byte string
            filter_['field'] = 'memo'
                filter_['value'] = bytes.fromhex(filter_['value'])
            except ValueError as e:
                raise APIError("Invalid memo_hex value")
def adjust_get_sends_results(query_result):
    """Format the memo_hex field.  Try and decode the memo from a utf-8 uncoded string. Invalid utf-8 strings return an empty memo."""
    filtered_results = []
    for send_row in list(query_result):
            if send_roM
                send_row['memo_hex'] = None
                send_row['memo'] = None
                if type(send_row['memo']) == str:
                    send_row['memo'] = bytes(send_row['memo'], 'utf-8')

                send_row['memo_hex'] = binascii.hexlify(send_row['memo']).decode('utf8')
                send_row['memo'] = send_row['memo'].decode('utf-8')
        except UnicodeDecodeError:
            send_row['memo'] = ''
        filtered_results.appenM
    return filtered_results
def adjust_get_transactions_results(query_result):
    """Format the data field.  Try and decode the data from a utf-8 uncoded string. Invalid utf-8 strings return an empty data."""
    filtered_results = []
    for transaction_row in list(query_result):
        transaction_row['data'] = transaction_row['data'].hex()
        filtered_results.append(transaction_row)
    return filtered_results
def compose_transaction(db, name, params,
                        encoding='auto',M
                        fee_per_kb=None,
                        estimate_fee_per_kb=None, estimate_fee_per_kb_conf_target=config.ESTIMATE_FEE_CONF_TARGET, estimate_fee_per_kb_mode=config.ESTIMATE_FEE_MODE,
                        regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE,
                        multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE,
                        op_return_value=config.DEFAULT_OP_RETURN_VALUE,
                        pubkey=None,
                        allow_unconfirmed_inputs=M
                        fee=None,
                        fee_provided=0,
                        unspent_tx_hash=None, custom_inputs=None, dust_return_pubkey=None, disable_utxo_locks=False, extended_tx_info=False,
                        p2sh_source_multisig_pubkeys=None, p2sh_source_multisig_pubkeys_required=None,
                        p2sh_pretx_txid=None, old_style_api=True, segwit=False):
    """Create and return a transaction."""
    # Get provided pubkeys.
    if type(pubkey) == str:
ed_pubkeys = [pubkey]
    elif type(pubkey) == list:
        provided_pubkeys = pubkey
    elif pubkey == None:
        provided_pubkeys = []
        assert False
    # Get additional pubkeys from `source` and `destination` params.
    # Convert `source` and `destination` to pubkeyhash form.
    for address_name in ['source', 'destination']:
        if address_name in params:
            address = params[address_name]
            if isinstance(address, list):
                #pkhshs = []
#for addr in address:
                #    provided_pubkeys += script.extract_pubkeys(addr)
                #    pkhshs.append(script.make_pubkeyhash(addr))
                #params[address_name] = pkhshs
                pass
                provided_pubkeys += script.extract_pubkeys(address)
                params[address_name] = script.make_pubkeyhash(address)
    # Check validity of collected pubkeys.
    for pubkey in provided_pubkeys:
        if not script.is_fully_valid(binascii.unhexlify(puM
            raise script.AddressError('invalid public key: {}'.format(pubkey))
    compose_method = sys.modules['counterpartylib.lib.messages.{}'.format(name)].compose
    compose_params = inspect.getargspec(compose_method)[0]
    missing_params = [p for p in compose_params if p not in params and p != 'db']
    for param in missing_params:
        params[param] = None
    # dont override fee_per_kb if specified
    if fee_per_kb is not None:
        estimate_fee_per_kb = False
 = config.DEFAULT_FEE_PER_KB
    if 'extended_tx_info' in params:
      extended_tx_info = params['extended_tx_info']
      del params['extended_tx_info']
    if 'old_style_api' in params:
        old_style_api = params['old_style_api']
        del params['old_style_api']
    if 'segwit' in params:
        segwit = params['segwit']
        del params['segwit']
    tx_info = compose_method(db, **params)
    return transaction.construct(db, tx_info, encoding=encoding,
                                        fee_pM
                                        estimate_fee_per_kb=estimate_fee_per_kb, estimate_fee_per_kb_conf_target=estimate_fee_per_kb_conf_target,
                                        regular_dust_size=regular_dust_size,
                                        multisig_dust_size=multisig_dust_size,
                                        op_return_value=op_return_value,
                                        provided_pubkeys=provided_pubkeys,
                                        allow_unconfM
irmed_inputs=allow_unconfirmed_inputs,
                                        exact_fee=fee,
                                        fee_provided=fee_provided,
                                        unspent_tx_hash=unspent_tx_hash, custom_inputs=custom_inputs,
                                        dust_return_pubkey=dust_return_pubkey,
                                        disable_utxo_locks=disable_utxo_locks,
                                        extended_tx_info=extended_tx_info,
                        M
                p2sh_source_multisig_pubkeys=p2sh_source_multisig_pubkeys, p2sh_source_multisig_pubkeys_required=p2sh_source_multisig_pubkeys_required,
                                        p2sh_pretx_txid=p2sh_pretx_txid,
                                        old_style_api=old_style_api,
                                        segwit=segwit)
def conditional_decorator(decorator, condition):
    """Checks the condition and if True applies specified decorator."""
    def gen_decorator(f):
        if not conditioM
            return f
        return decorator(f)
    return gen_decorator
def init_api_access_log(app):
    """Initialize API logger."""
    loggers = (logging.getLogger('werkzeug'), app.logger)
    # Disable console logging...
    for l in loggers:
        l.setLevel(logging.INFO)
        l.propagate = False
    # Log to file, if configured...
    if config.API_LOG:
        handler = logging_handlers.RotatingFileHandler(config.API_LOG, 'a', API_MAX_LOG_SIZE, API_MAX_LOG_COUNT)
        for l in loggers:
       l.addHandler(handler)
class APIStatusPoller(threading.Thread):
    """Perform regular checks on the state of the backend and the database."""
    def __init__(self):
        self.last_database_check = 0
        threading.Thread.__init__(self)
        self.stop_event = threading.Event()
        self.stop_event.set()
        logger.debug('Starting API Status Poller.')
        global current_api_status_code, current_api_status_response_json
        db = database.get_conM
nection(read_only=True, integrity_check=False)
        while self.stop_event.is_set() != True:
                # Check that backend is running, communicable, and caught up with the blockchain.
                # Check that the database has caught up with bitcoind.
                if time.time() - self.last_database_check > 10 * 60: # Ten minutes since last check.
                    if not config.FORCE:
                        code = 11
                        logger.debug('Checking backend state.'M
                        check_backend_state()
                        code = 12
                        logger.debug('Checking database state.')
                        check_database_state(db, backend.getblockcount())
                        self.last_database_check = time.time()
            except (BackendError, DatabaseError) as e:
                exception_name = e.__class__.__name__
                exception_text = str(e)
                logger.debug("API Status Poller: %s", exception_text)
sonrpc_response = jsonrpc.exceptions.JSONRPCServerError(message=exception_name, data=exception_text)
                current_api_status_code = code
                current_api_status_response_json = jsonrpc_response.json.encode()
                current_api_status_code = None
                current_api_status_response_json = None
            time.sleep(config.BACKEND_POLL_INTERVAL)
class APIServer(threading.Thread):
    """Handle JSON-RPC API calls."""
    def __init__(self, db=None):
        self.is_ready = False
        threading.Thread.__init__(self)
        self.stop_event = threading.Event()
        self.stop_event.set()
        logger.info('Starting API Server.')
        self.db = self.db or database.get_connection(read_only=True, integrity_check=False)
        app = flask.Flask(__name__)
        auth = HTTPBasicAuth()
        @auth.get_password
        def get_pw(username):
            if username == config.RPC_USER:M
                return config.RPC_PASSWORD
            return None
        ######################
        # Generate dynamically get_{table} methods
        def generate_get_method(table):
            def get_method(**kwargs):
                try:
                    return get_rows(self.db, table=table, **kwargs)
                except TypeError as e:          #TODO: generalise for all API methods
                    raise APIError(str(e))
            return get_method
        for table in APM
            new_method = generate_get_method(table)
            new_method.__name__ = 'get_{}'.format(table)
            dispatcher.add_method(new_method)
        @dispatcher.add_method
        def sql(query, bindings=None):
            if bindings == None:
                bindings = []
            return db_query(self.db, query, tuple(bindings))
        ######################
        #WRITE/ACTION API
        # Generate dynamically create_{transaction} methods
        def generate_create_method(tx):
            def split_params(**kwargs):
                transaction_args = {}
                common_args = {}
                private_key_wif = None
                for key in kwargs:
                    if key in COMMONS_ARGS:
                        common_args[key] = kwargs[key]
                    elif key == 'privkey':
                        private_key_wif = kwargs[key]
                    else:
                        transaction_args[key] = kwargs[key]
                return transaction_args, common_argsM
            def create_method(**kwargs):
                try:
                    transaction_args, common_args, private_key_wif = split_params(**kwargs)
                    return compose_transaction(self.db, name=tx, params=transaction_args, **common_args)
                except (TypeError, script.AddressError, exceptions.ComposeError, exceptions.TransactionError, exceptions.BalanceError) as error:
                    # TypeError happens when unexpected keyword arguments are passed in
           error_msg = "Error composing {} transaction via API: {}".format(tx, str(error))
                    logging.warning(error_msg)
                    logging.warning(traceback.format_exc())
                    raise JSONRPCDispatchException(code=JSON_RPC_ERROR_API_COMPOSE, message=error_msg)
            return create_method
        for tx in API_TRANSACTIONS:
            create_method = generate_create_method(tx)
            create_method.__name__ = 'create_{}'.format(tx)
            dispatcher.add_methodM
        @dispatcher.add_method
        def get_messages(block_index):
            if not isinstance(block_index, int):
                raise APIError("block_index must be an integer.")
            cursor = self.db.cursor()
            cursor.execute('select * from messages where block_index = ? order by message_index asc', (block_index,))
            messages = cursor.fetchall()
            cursor.close()
            return messages
        @dispatcher.add_method
        def get_messages_by_indexM
            """Get specific messages from the feed, based on the message_index.
            @param message_index: A single index, or a list of one or more message indexes to retrieve.
            if not isinstance(message_indexes, list):
                message_indexes = [message_indexes,]
            for idx in message_indexes:  #make sure the data is clean
                if not isinstance(idx, int):
                    raise APIError("All items in message_indexes are not integM
            cursor = self.db.cursor()
            cursor.execute('SELECT * FROM messages WHERE message_index IN (%s) ORDER BY message_index ASC'
                % (','.join([str(x) for x in message_indexes]),))
            messages = cursor.fetchall()
            cursor.close()
            return messages
        @dispatcher.add_method
        def get_supply(asset):
            if asset == 'BTC':
                return  backend.get_btc_supply(normalize=False)
            elif asset == 'XCP':
 return util.xcp_supply(self.db)
                asset = util.resolve_subasset_longname(self.db, asset)
                return util.asset_supply(self.db, asset)
        @dispatcher.add_method
        def get_xcp_supply():
            logger.warning("Deprecated method: `get_xcp_supply`")
            return util.xcp_supply(self.db)
        @dispatcher.add_method
        def get_asset_info(assets=None, asset=None):
            if asset is not None:
                assets = [asset]
        if not isinstance(assets, list):
                raise APIError("assets must be a list of asset names, even if it just contains one entry")
            assetsInfo = []
            for asset in assets:
                asset = util.resolve_subasset_longname(self.db, asset)
                # BTC and XCP.
                if asset in [config.BTC, config.XCP]:
                    if asset == config.BTC:
                        supply = backend.get_btc_supply(normalize=False)
                    else:
             supply = util.xcp_supply(self.db)
                    assetsInfo.append({
                        'asset': asset,
                        'asset_longname': None,
                        'owner': None,
                        'divisible': True,
                        'locked': False,
                        'supply': supply,
                        'description': '',
                        'issuer': None
                    })
                    continue
                # User
            cursor = self.db.cursor()
                issuances = list(cursor.execute('''SELECT * FROM issuances WHERE (status = ? AND asset = ?) ORDER BY block_index ASC''', ('valid', asset)))
                cursor.close()
                if not issuances:
                    continue #asset not found, most likely
                else:
                    last_issuance = issuances[-1]
                locked = False
                for e in issuances:
                    if e['locked']: locked = True
   assetsInfo.append({
                    'asset': asset,
                    'asset_longname': last_issuance['asset_longname'],
                    'owner': last_issuance['issuer'],
                    'divisible': bool(last_issuance['divisible']),
                    'locked': locked,
                    'supply': util.asset_supply(self.db, asset),
                    'description': last_issuance['description'],
                    'issuer': last_issuance['issuer']})
            return assetsInfo
        def get_block_info(block_index):
            assert isinstance(block_index, int)
            cursor = self.db.cursor()
            cursor.execute('''SELECT * FROM blocks WHERE block_index = ?''', (block_index,))
            blocks = list(cursor)
            if len(blocks) == 1:
                block = blocks[0]
            elif len(blocks) == 0:
                raise exceptions.DatabaseError('No blocks found.')
                assert False
            cursor.close()
        @dispatcher.add_method
        def fee_per_kb(conf_target=config.ESTIMATE_FEE_CONF_TARGET, mode=config.ESTIMATE_FEE_MODE):
            return backend.fee_per_kb(conf_target, mode)
        @dispatcher.add_method
        def get_blocks(block_indexes, min_message_index=None):
            """fetches block info and messages for the specified block indexes
            @param min_message_index: Retrieve blocks from the message feed on or after this specific message index
              (useful M
since blocks may appear in the message feed more than once, if a reorg occurred). Note that
              if this parameter is not specified, the messages for the first block will be returned.
            if not isinstance(block_indexes, (list, tuple)):
                raise APIError("block_indexes must be a list of integers.")
            if len(block_indexes) >= 250:
                raise APIError("can only specify up to 250 indexes at a time.")
            block_indexes_str = ','.join([str(x) foM
r x in block_indexes])
            cursor = self.db.cursor()
            # The blocks table gets rolled back from undolog, so min_message_index doesn't matter for this query
            cursor.execute('SELECT * FROM blocks WHERE block_index IN (%s) ORDER BY block_index ASC'
                % (block_indexes_str,))
            blocks = cursor.fetchall()
            cursor.execute('SELECT * FROM messages WHERE block_index IN (%s) ORDER BY message_index ASC'
                % (block_indexes_str,))
            messageM
s = collections.deque(cursor.fetchall())
            # Discard any messages less than min_message_index
            if min_message_index:
                while len(messages) and messages[0]['message_index'] < min_message_index:
                    messages.popleft()
            # Packages messages into their appropriate block in the data structure to be returned
            for block in blocks:
                block['_messages'] = []
                while len(messages) and messages[0]['block_index'] == block['bloM
                    block['_messages'].append(messages.popleft())
            #NOTE: if len(messages), then we're only returning the messages for the first set of blocks before the reorg
            cursor.close()
            return blocks
        @dispatcher.add_method
        def get_running_info():
            latestBlockIndex = backend.getblockcount()
                check_database_state(self.db, latestBlockIndex)
            except DatabaseError:
                caught_up = FalsM
                caught_up = True
                cursor = self.db.cursor()
                blocks = list(cursor.execute('''SELECT * FROM blocks WHERE block_index = ?''', (util.CURRENT_BLOCK_INDEX, )))
                assert len(blocks) == 1
                last_block = blocks[0]
                cursor.close()
                last_block = None
                last_message = util.last_message(self.db)
                last_M
                indexd_blocks_behind = backend.getindexblocksbehind()
                indexd_blocks_behind = latestBlockIndex if latestBlockIndex > 0 else 999999
            indexd_caught_up = indexd_blocks_behind <= 1
            server_ready = caught_up and indexd_caught_up
            return {
                'server_ready': server_ready,
                'db_caught_up': caught_up,
                'bitcoin_block_count': latestBlockIndex,
                'lastM
_block': last_block,
                'indexd_caught_up': indexd_caught_up,
                'indexd_blocks_behind': indexd_blocks_behind,
                'last_message_index': last_message['message_index'] if last_message else -1,
                'api_limit_rows': config.API_LIMIT_ROWS,
                'running_testnet': config.TESTNET,
                'running_regtest': config.REGTEST,
                'running_testcoin': config.TESTCOIN,
                'version_major': config.VERSION_MAJOR,
                'versioM
n_minor': config.VERSION_MINOR,
                'version_revision': config.VERSION_REVISION
        @dispatcher.add_method
        def get_element_counts():
            counts = {}
            cursor = self.db.cursor()
            for element in ['transactions', 'blocks', 'debits', 'credits', 'balances', 'sends', 'orders',
                'order_matches', 'btcpays', 'issuances', 'broadcasts', 'bets', 'bet_matches', 'dividends',
                'burns', 'cancels', 'order_expirations', 'bet_expirationsM
', 'order_match_expirations',
                'bet_match_expirations', 'messages', 'destructions']:
                cursor.execute("SELECT COUNT(*) AS count FROM %s" % element)
                count_list = cursor.fetchall()
                assert len(count_list) == 1
                counts[element] = count_list[0]['count']
            cursor.close()
            return counts
        @dispatcher.add_method
        def get_asset_names(longnames=False):
            cursor = self.db.cursor()
            if longnames:
                names = []
                for row in cursor.execute("SELECT asset, asset_longname FROM issuances WHERE status = 'valid' GROUP BY asset ORDER BY asset ASC"):
                    names.append({'asset': row['asset'], 'asset_longname': row['asset_longname']})
                names = [row['asset'] for row in cursor.execute("SELECT DISTINCT asset FROM issuances WHERE status = 'valid' ORDER BY asset ASC")]
            cursor.close()
            return names
        @dispatcher.add_methoM
        def get_asset_longnames():
            return get_asset_names(longnames=True)
        @dispatcher.add_method
        def get_holder_count(asset):
            asset = util.resolve_subasset_longname(self.db, asset)
            holders = util.holders(self.db, asset, True)
            addresses = []
            for holder in holders:
                addresses.append(holder['address'])
            return {asset: len(set(addresses))}
        @dispatcher.add_method
        def get_holders(asset):
sset = util.resolve_subasset_longname(self.db, asset)
            holders = util.holders(self.db, asset, True)
            return holders
        @dispatcher.add_method
        def search_raw_transactions(address, unconfirmed=True):
            return backend.search_raw_transactions(address, unconfirmed=unconfirmed)
        @dispatcher.add_method
        def get_unspent_txouts(address, unconfirmed=False, unspent_tx_hash=None, order_by=None):
            results = backend.get_unspent_txouts(address, unconfirmed=unM
confirmed, unspent_tx_hash=unspent_tx_hash)
            if order_by is None:
                return results
                order_key = order_by
                reverse = False
                if order_key.startswith('-'):
                    order_key = order_key[1:]
                    reverse = True
                return sorted(results, key=lambda x: x[order_key], reverse=reverse)
        @dispatcher.add_method
        def getrawtransaction(tx_hash, verbose=False, skip_missing=False):
      return backend.getrawtransaction(tx_hash, verbose=verbose, skip_missing=skip_missing)
        @dispatcher.add_method
        def getrawtransaction_batch(txhash_list, verbose=False, skip_missing=False):
            return backend.getrawtransaction_batch(txhash_list, verbose=verbose, skip_missing=skip_missing)
        @dispatcher.add_method
        def get_tx_info(tx_hex, block_index=None):
            # block_index mandatory for transactions before block 335000
            source, destination, btc_amount, feM
e, data, extra = blocks.get_tx_info(tx_hex, block_index=block_index)
            return source, destination, btc_amount, fee, util.hexlify(data) if data else ''
        @dispatcher.add_method
        def unpack(data_hex):
            data = binascii.unhexlify(data_hex)
            message_type_id, message = message_type.unpack(data)
            # TODO: Enabled only for `send`.
            if message_type_id == send.ID:
                unpack_method = send.unpack
            elif message_type_id == enhanced_send.IM
                unpack_method = enhanced_send.unpack
                raise APIError('unsupported message type')
            unpacked = unpack_method(self.db, message, util.CURRENT_BLOCK_INDEX)
            return message_type_id, unpacked
        @dispatcher.add_method
        # TODO: Rename this method.
        def search_pubkey(pubkeyhash, provided_pubkeys=None):
            return backend.pubkeyhash_to_pubkey(pubkeyhash, provided_pubkeys=provided_pubkeys)
        @dispatcher.add_method
    def get_dispenser_info(tx_hash=None, tx_index=None):
            cursor = self.db.cursor()
            if tx_hash is None and tx_index is None:
                raise APIError("You must provided a tx hash or a tx index")
            if tx_hash is not None:
                cursor.execute('SELECT d.*, a.asset_longname FROM dispensers d LEFT JOIN assets a ON a.asset_name = d.asset WHERE tx_hash=:tx_hash', {"tx_hash":tx_hash})
                cursor.execute('SELECT d.*, a.M
asset_longname FROM dispensers d LEFT JOIN assets a ON a.asset_name = d.asset WHERE tx_index=:tx_index', {"tx_index":tx_index})
            dispensers = cursor.fetchall()
            if len(dispensers) == 1:
                dispenser = dispensers[0]
                oracle_price = ""
                satoshi_price = ""
                fiat_price = ""
                oracle_price_last_updated = ""
                oracle_fiat_label = ""
                if dispenser["oracle_addM
                    fiat_price = util.satoshirate_to_fiat(dispenser["satoshirate"])
                    oracle_price, oracle_fee, oracle_fiat_label, oracle_price_last_updated = util.get_oracle_last_price(self.db, dispenser["oracle_address"], util.CURRENT_BLOCK_INDEX)

                    if (oracle_price > 0):
                        satoshi_price = math.ceil((fiat_price/oracle_price) * config.UNIT)
                    else:
                        raise APIError("Last oracle priM
                return {
                    "tx_index": dispenser["tx_index"],
                    "tx_hash": dispenser["tx_hash"],
                    "block_index": dispenser["block_index"],
                    "source": dispenser["source"],
                    "asset": dispenser["asset"],
                    "give_quantity": dispenser["give_quantity"],
                    "escrow_quantity": dispenser["escrow_quantity"],
                    "mainchainrate": dispenser["satoshirate"],M
                    "fiat_price": fiat_price,
                    "fiat_unit": oracle_fiat_label,
                    "oracle_price": oracle_price,
                    "satoshi_price": satoshi_price,
                    "status": dispenser["status"],
                    "give_remaining": dispenser["give_remaining"],
                    "oracle_address": dispenser["oracle_address"],
                    "oracle_price_last_updated": oracle_price_last_updated,
                    "asset_longname": dispenser["asset_lonM
            return {}
        def _set_cors_headers(response):
            if not config.RPC_NO_ALLOW_CORS:
                response.headers['Access-Control-Allow-Origin'] = '*'
                response.headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
                response.headers['Access-Control-Allow-Headers'] = 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'
('/healthz', methods=['GET'])
        def handle_healthz():
            msg, code = 'Healthy', 200
                latestBlockIndex = backend.getblockcount()
                check_database_state(self.db, latestBlockIndex)
            except DatabaseError:
                msg, code = 'Unhealthy', 503
            return flask.Response(msg, code, mimetype='text/plain')
        @app.route('/', defaults={'args_path': ''}, methods=['GET', 'POST', 'OPTIONS'])
        @app.route('/<path:args_path>',  methM
ods=['GET', 'POST', 'OPTIONS'])
        # Only require authentication if RPC_PASSWORD is set.
        @conditional_decorator(auth.login_required, hasattr(config, 'RPC_PASSWORD'))
        def handle_root(args_path):
            """Handle all paths, decide where to forward the query."""
            if args_path == '' or args_path.startswith('api/') or args_path.startswith('API/') or \
               args_path.startswith('rpc/') or args_path.startswith('RPC/'):
                if flask.request.method == 'POST':
              # Need to get those here because it might not be available in this aux function.
                    request_json = flask.request.get_data().decode('utf-8')
                    response = handle_rpc_post(request_json)
                    return response
                elif flask.request.method == 'OPTIONS':
                    response = handle_rpc_options()
                    return response
                else:
                    error = 'Invalid method.'
                    return flask.ResponsM
e(error, 405, mimetype='application/json')
            elif args_path.startswith('rest/') or args_path.startswith('REST/'):
                if flask.request.method == 'GET' or flask.request.method == 'POST':
                    # Pass the URL path without /REST/ part and Flask request object.
                    rest_path = args_path.split('/', 1)[1]
                    response = handle_rest(rest_path, flask.request)
                    return response
                else:
                    error = 'Invalid metM
                    return flask.Response(error, 405, mimetype='application/json')
                # Not found
                return flask.Response(None, 404, mimetype='application/json')
        ######################
        # JSON-RPC API
        ######################
        def handle_rpc_options():
            response = flask.Response('', 204)
            _set_cors_headers(response)
            return response
        def handle_rpc_post(request_json):
            """Handle /API/ M
POST route. Call relevant get_rows/create_transaction wrapper."""
            # Check for valid request format.
                request_data = json.loads(request_json)
                assert 'id' in request_data and request_data['jsonrpc'] == "2.0" and request_data['method']
                # params may be omitted
                obj_error = jsonrpc.exceptions.JSONRPCInvalidRequest(data="Invalid JSON-RPC 2.0 request format")
                return flask.Response(obj_error.json.eM
ncode(), 400, mimetype='application/json')
            # Only arguments passed as a `dict` are supported.
            if request_data.get('params', None) and not isinstance(request_data['params'], dict):
                obj_error = jsonrpc.exceptions.JSONRPCInvalidRequest(
                    data='Arguments must be passed as a JSON object (list of unnamed arguments not supported)')
                return flask.Response(obj_error.json.encode(), 400, mimetype='application/json')
            # Return an error if thM
e API Status Poller checks fail.
            if not config.FORCE and current_api_status_code:
                return flask.Response(current_api_status_response_json, 503, mimetype='application/json')
            # Answer request normally.
            # NOTE: `UnboundLocalError: local variable 'output' referenced before assignment` means the method doesn
            jsonrpc_response = jsonrpc.JSONRPCResponseManager.handle(request_json, dispatcher)
            response = flask.Response(jsonrpc_rM
esponse.json.encode(), 200, mimetype='application/json')
            _set_cors_headers(response)
            return response
        ######################
        # HTTP REST API
        ######################
        def handle_rest(path_args, flask_request):
            """Handle /REST/ route. Query the database using get_rows or create transaction using compose_transaction."""
            url_action = flask_request.path.split('/')[-1]
            if url_action == 'compose':
                compose = True
       elif url_action == 'get':
                compose = False
                error = 'Invalid action "%s".' % url_action
                return flask.Response(error, 400, mimetype='application/json')
            # Get all arguments passed via URL.
            url_args = path_args.split('/')
                query_type = url_args.pop(0).lower()
            except IndexError:
                error = 'No query_type provided.'
                return flask.Response(error, 400, mimeM
type='application/json')
            # Check if message type or table name are valid.
            if (compose and query_type not in API_TRANSACTIONS) or \
               (not compose and query_type not in API_TABLES):
                error = 'No such query type in supported queries: "%s".' % query_type
                return flask.Response(error, 400, mimetype='application/json')
            # Parse the additional arguments.
            extra_args = flask_request.args.items()
            query_data = {}
                common_args = {}
                transaction_args = {}
                for (key, value) in extra_args:
                    # Determine value type.
                    try:
                        value = int(value)
                    except ValueError:
                        try:
                            value = float(value)
                        except ValueError:
                            pass
                    # Split keys into common and transaction-specific arguments. M
Discard the privkey.
                    if key in COMMONS_ARGS:
                        common_args[key] = value
                    elif key == 'privkey':
                        pass
                    else:
                        transaction_args[key] = value
                # Must have some additional transaction arguments.
                if not len(transaction_args):
                    error = 'No transaction arguments provided.'
                    return flask.Response(error, 400, mimetype='applicationM
                # Compose the transaction.
                try:
                    query_data = compose_transaction(self.db, name=query_type, params=transaction_args, **common_args)
                except (script.AddressError, exceptions.ComposeError, exceptions.TransactionError, exceptions.BalanceError) as error:
                    error_msg = logging.warning("{} -- error composing {} transaction via API: {}".format(
                        str(error.__class__.__name__), query_type, str(error)))
             return flask.Response(error_msg, 400, mimetype='application/json')
                # Need to de-generate extra_args to pass it through.
                query_args = dict([item for item in extra_args])
                operator = query_args.pop('op', 'AND')
                # Put the data into specific dictionary format.
                data_filter = [{'field': key, 'op': '==', 'value': value} for (key, value) in query_args.items()]
                # Run the query.
                try:
                    query_data = get_rows(self.db, table=query_type, filters=data_filter, filterop=operator)
                except APIError as error:
                    return flask.Response(str(error), 400, mimetype='application/json')
            # See which encoding to choose from.
            file_format = flask_request.headers['Accept']
            # JSON as default.
            if file_format == 'application/json' or file_format == '*/*':
                response_data = json.dumps(query_data)
lif file_format == 'application/xml':
                # Add document root for XML. Note when xmltodict encounters a list, it produces separate tags for every item.
                # Hence we end up with multiple query_type roots. To combat this we put it in a separate item dict.
                response_data = serialize_to_xml({query_type: {'item': query_data}})
                error = 'Invalid file format: "%s".' % file_format
                return flask.Response(error, 400, mimetype='applicatioM
            response = flask.Response(response_data, 200, mimetype=file_format)
            return response
        # Init the HTTP Server.
        init_api_access_log(app)
        # Run app server (blocking)
        self.is_ready = True
        app.run(host=config.RPC_HOST, port=config.RPC_PORT, threaded=True)
        self.db.close()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
from Crypto.Cipher import ARC4
def init_arc4(seed):
isinstance(seed, str):
        seed = binascii.unhexlify(seed)
    return ARC4.new(seed)
Initialise database.
Sieve blockchain for Counterparty transactions, and add them to the database.
logger = logging.getLogger(__name__)
import bitcoin as bitcoinlib
from bitcoin.core.script import CScriptInvaliM
from counterpartylib.lib import config
from counterpartylib.lib import exceptions
from counterpartylib.lib import util
from counterpartylib.lib import check
from counterpartylib.lib import script
from counterpartylib.lib import backend
from counterpartylib.lib import log
from counterpartylib.lib import database
from counterpartylib.lib import message_type
from counterpartylib.lib import arc4
from counterpartylib.lib.transaction_helper import p2sh_encoding
from .messages import (send, order, btcpay, issuancM
e, broadcast, bet, dividend, burn, cancel, rps, rpsresolve, destroy, sweep, dispenser)
from .messages.versions import enhanced_send, mpma
from .kickstart.blocks_parser import BlockchainParser, ChainstateParser
from .kickstart.utils import ib2h
from .exceptions import DecodeError, BTCOnlyError
# Order matters for FOREIGN KEY constraints.
TABLES = ['credits', 'debits', 'messages'] + \
         ['bet_match_resolutions', 'order_match_expirations', 'order_matches',
         'order_expirations', 'orders', 'bet_match_eM
xpirations', 'bet_matches',
         'bet_expirations', 'bets', 'broadcasts', 'btcpays', 'burns',
         'cancels', 'dividends', 'issuances', 'sends',
         'rps_match_expirations', 'rps_expirations', 'rpsresolves',
         'rps_matches', 'rps',
         'destructions', 'assets', 'addresses', 'sweeps', 'dispensers', 'dispenses']
# Compose list of tables tracked by undolog
UNDOLOG_TABLES = copy.copy(TABLES)
UNDOLOG_TABLES.remove('messages')
UNDOLOG_TABLES += ['balances']
CURR_DIR = os.path.dirname(os.path.reaM
with open(CURR_DIR + '/../mainnet_burns.csv', 'r') as f:
    mainnet_burns_reader = csv.DictReader(f)
    MAINNET_BURNS = {}
    for line in mainnet_burns_reader:
        MAINNET_BURNS[line['tx_hash']] = line
def parse_tx(db, tx):
    """Parse the transaction, return True for success."""
    cursor = db.cursor()
            # Only one source and one destination allowed for now.
            if len(tx['source'].split('-')) > 1:
                return
            if tx['desM
                if len(tx['destination'].split('-')) > 1:
                    return
            # Burns.
            if tx['destination'] == config.UNSPENDABLE:
                burn.parse(db, tx, MAINNET_BURNS)
                return
            if len(tx['data']) > 1:
                try:
                    message_type_id, message = message_type.unpack(tx['data'], tx['block_index'])
                except struct.error:    # Deterministically raised.
                    message_type_id = None
               message = None
                message_type_id = None
                message = None
            # Protocol change.
            rps_enabled = tx['block_index'] >= 308500 or config.TESTNET or config.REGTEST
            if message_type_id == send.ID:
                send.parse(db, tx, message)
            elif message_type_id == enhanced_send.ID and util.enabled('enhanced_sends', block_index=tx['block_index']):
                enhanced_send.parse(db, tx, message)
            elif meM
ssage_type_id == mpma.ID and util.enabled('mpma_sends', block_index=tx['block_index']):
                mpma.parse(db, tx, message)
            elif message_type_id == order.ID:
                order.parse(db, tx, message)
            elif message_type_id == btcpay.ID:
                btcpay.parse(db, tx, message)
            elif message_type_id == issuance.ID:
                issuance.parse(db, tx, message, message_type_id)
            elif message_type_id == issuance.SUBASSET_ID and util.enabled('subassets', bloM
ck_index=tx['block_index']):
                issuance.parse(db, tx, message, message_type_id)
            elif message_type_id == broadcast.ID:
                broadcast.parse(db, tx, message)
            elif message_type_id == bet.ID:
                bet.parse(db, tx, message)
            elif message_type_id == dividend.ID:
                dividend.parse(db, tx, message)
            elif message_type_id == cancel.ID:
                cancel.parse(db, tx, message)
            elif message_type_id == rps.ID and rpsM
                rps.parse(db, tx, message)
            elif message_type_id == rpsresolve.ID and rps_enabled:
                rpsresolve.parse(db, tx, message)
            elif message_type_id == destroy.ID and util.enabled('destroy_reactivated', block_index=tx['block_index']):
                destroy.parse(db, tx, message)
            elif message_type_id == sweep.ID and util.enabled('sweep_send', block_index=tx['block_index']):
                sweep.parse(db, tx, message)
            elif message_type_iM
d == dispenser.ID and util.enabled('dispensers', block_index=tx['block_index']):
                dispenser.parse(db, tx, message)
            elif message_type_id == dispenser.DISPENSE_ID and util.enabled('dispensers', block_index=tx['block_index']):
                dispenser.dispense(db, tx)
                cursor.execute('''UPDATE transactions \
                                           SET supported=? \
                                           WHERE tx_hash=?''',
                            M
            (False, tx['tx_hash']))
                if tx['block_index'] != config.MEMPOOL_BLOCK_INDEX:
                    logger.info('Unsupported transaction: hash {}; data {}'.format(tx['tx_hash'], tx['data']))
                cursor.close()
                return False
            # NOTE: for debugging (check asset conservation after every `N` transactions).
            # if not tx['tx_index'] % N:
            #     check.asset_conservation(db)
            return True
    except Exception as e:
 exceptions.ParseTransactionError("%s" % e)
        cursor.close()
def parse_block(db, block_index, block_time,
                previous_ledger_hash=None, ledger_hash=None,
                previous_txlist_hash=None, txlist_hash=None,
                previous_messages_hash=None):
    """Parse the block, return hash of new ledger, txlist and messages.
    The unused arguments `ledger_hash` and `txlist_hash` are for the test suite.
    undolog_cursor = db.cursor()
    #remove the row tracer andM
 exec tracer on this cursor, so we don't utilize them with undolog operations...
    undolog_cursor.setexectrace(None)
    undolog_cursor.setrowtrace(None)
    util.BLOCK_LEDGER = []
    database.BLOCK_MESSAGES = []
    assert block_index == util.CURRENT_BLOCK_INDEX
    # Remove undolog records for any block older than we should be tracking
    undolog_oldest_block_index = block_index - config.UNDOLOG_MAX_PAST_BLOCKS
    first_undo_index = list(undolog_cursor.execute('''SELECT first_undo_index FROM undolog_blockM
 WHERE block_index == ?''',
        (undolog_oldest_block_index,)))
    if len(first_undo_index) == 1 and first_undo_index[0] is not None:
        undolog_cursor.execute('''DELETE FROM undolog WHERE undo_index < ?''', (first_undo_index[0][0],))
    undolog_cursor.execute('''DELETE FROM undolog_block WHERE block_index < ?''',
        (undolog_oldest_block_index,))
    # Set undolog barrier for this block
    if block_index != config.BLOCK_FIRST:
        undolog_cursor.execute('''INSERT OR REPLACE INTO undolog_blockM
(block_index, first_undo_index)
            SELECT ?, seq+1 FROM SQLITE_SEQUENCE WHERE name='undolog' ''', (block_index,))
        undolog_cursor.execute('''INSERT OR REPLACE INTO undolog_block(block_index, first_undo_index)
            VALUES(?,?)''', (block_index, 1,))
    undolog_cursor.close()
    # Expire orders, bets and rps.
    order.expire(db, block_index)
    bet.expire(db, block_index, block_time)
    rps.expire(db, block_index)
    # Parse transactions, sorting them by type.
    cursor.execute('''SELECT * FROM transactions \
                      WHERE block_index=? ORDER BY tx_index''',
                   (block_index,))
    for tx in list(cursor):
            parse_tx(db, tx)
            txlist.append('{}{}{}{}{}{}'.format(tx['tx_hash'], tx['source'], tx['destination'],
                                                tx['btc_amount'], tx['fee'],
                                                binascii.hexlify(tx['data']).decode('UTF-8')))
 except exceptions.ParseTransactionError as e:
            logger.warn('ParseTransactionError for tx %s: %s' % (tx['tx_hash'], e))
    # Calculate consensus hashes.
    new_txlist_hash, found_txlist_hash = check.consensus_hash(db, 'txlist_hash', previous_txlist_hash, txlist)
    new_ledger_hash, found_ledger_hash = check.consensus_hash(db, 'ledger_hash', previous_ledger_hash, util.BLOCK_LEDGER)
    new_messages_hash, found_messages_hash = check.consensus_haM
sh(db, 'messages_hash', previous_messages_hash, database.BLOCK_MESSAGES)
    return new_ledger_hash, new_txlist_hash, new_messages_hash, found_messages_hash
    """Initialise data, create and populate the database."""
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS blocks(
                      block_index INTEGER UNIQUE,
                      block_hash TEXT UNIQUE,
                      block_time INTEGER,
                      previous_block_hash TEXM
                      difficulty INTEGER,
                      PRIMARY KEY (block_index, block_hash))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON blocks (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      index_hash_idx ON blocks (block_index, block_hash)
                   ''')
t do `ALTER TABLE IF COLUMN NOT EXISTS`.
    columns = [column['name'] for column M
in cursor.execute('''PRAGMA table_info(blocks)''')]
    if 'ledger_hash' not in columns:
        cursor.execute('''ALTER TABLE blocks ADD COLUMN ledger_hash TEXT''')
    if 'txlist_hash' not in columns:
        cursor.execute('''ALTER TABLE blocks ADD COLUMN txlist_hash TEXT''')
    if 'messages_hash' not in columns:
        cursor.execute('''ALTER TABLE blocks ADD COLUMN messages_hash TEXT''')
    if 'previous_block_hash' not in columns:
        cursor.execute('''ALTER TABLE blocks ADD COLUMN previous_block_hash TM
    if 'difficulty' not in columns:
        cursor.execute('''ALTER TABLE blocks ADD COLUMN difficulty TEXT''')
    # Check that first block in DB is BLOCK_FIRST.
    cursor.execute('''SELECT * from blocks ORDER BY block_index''')
    blocks = list(cursor)
        if blocks[0]['block_index'] != config.BLOCK_FIRST:
            raise exceptions.DatabaseError('First block in database is not block {}.'.format(config.BLOCK_FIRST))
    cursor.execute('''CREATE TABLE IF NOTM
 EXISTS transactions(
                      tx_index INTEGER UNIQUE,
                      tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      block_hash TEXT,
                      block_time INTEGER,
                      source TEXT,
                      destination TEXT,
                      btc_amount INTEGER,
                      fee INTEGER,
                      data BLOB,
                      supported BOOL DEFAULT 1,
                      FOREIGN KEY (block_index, bloM
ck_hash) REFERENCES blocks(block_index, block_hash),
                      PRIMARY KEY (tx_index, tx_hash, block_index))
                    ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON transactions (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      tx_index_idx ON transactions (tx_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      tx_hash_idx ON transactionsM
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      index_index_idx ON transactions (block_index, tx_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      index_hash_index_idx ON transactions (tx_index, tx_hash, block_index)
                   ''')
    # Purge database of blocks, transactions from before BLOCK_FIRST.
    cursor.execute('''DELETE FROM blocks WHERE block_index < ?''', (config.BLOCK_FIRST,))
or.execute('''DELETE FROM transactions WHERE block_index < ?''', (config.BLOCK_FIRST,))
    # (Valid) debits
    cursor.execute('''CREATE TABLE IF NOT EXISTS debits(
                      block_index INTEGER,
                      address TEXT,
                      asset TEXT,
                      quantity INTEGER,
                      action TEXT,
                      event TEXT,
                      FOREIGN KEY (block_index) REFERENCES blocks(block_index))
                   ''')
    cursor.execute('''CREAM
TE INDEX IF NOT EXISTS
                      address_idx ON debits (address)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      asset_idx ON debits (asset)
                   ''')
    # (Valid) credits
    cursor.execute('''CREATE TABLE IF NOT EXISTS credits(
                      block_index INTEGER,
                      address TEXT,
                      asset TEXT,
                      quantity INTEGER,
                      calling_function TEXT,
                      FOREIGN KEY (block_index) REFERENCES blocks(block_index))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      address_idx ON credits (address)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      asset_idx ON credits (asset)
                   ''')
    cursor.execute('''CREATE TABLE IF NOT EXISTS balances(
                      address TEXT,
                      asset TEXT,
                    quantity INTEGER)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      address_asset_idx ON balances (address, asset)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      address_idx ON balances (address)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      asset_idx ON balances (asset)
                   ''')
    # TODO: Store more asset info here?!
sor.execute('''CREATE TABLE IF NOT EXISTS assets(
                      asset_id TEXT UNIQUE,
                      asset_name TEXT UNIQUE,
                      block_index INTEGER,
                      asset_longname TEXT)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      name_idx ON assets (asset_name)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      id_idx ON assets (asset_id)
                   ''')
    # Add asset_lonM
gname for sub-assets
t do `ALTER TABLE IF COLUMN NOT EXISTS`.
    columns = [column['name'] for column in cursor.execute('''PRAGMA table_info(assets)''')]
    if 'asset_longname' not in columns:
        cursor.execute('''ALTER TABLE assets ADD COLUMN asset_longname TEXT''')
    cursor.execute('''CREATE UNIQUE INDEX IF NOT EXISTS asset_longname_idx ON assets(asset_longname)''')
    cursor.execute('''SELECT * FROM assets WHERE asset_name = ?''', ('BTC',))
    if not list(cursor):
execute('''INSERT INTO assets VALUES (?,?,?,?)''', ('0', 'BTC', None, None))
        cursor.execute('''INSERT INTO assets VALUES (?,?,?,?)''', ('1', 'XCP', None, None))
    # Leaving this here because in the future this could work for other things besides broadcast
    cursor.execute('''CREATE TABLE IF NOT EXISTS addresses(
                      address TEXT UNIQUE,
                      options INTEGER,
                      block_index INTEGER)
                   ''')
    cursor.execute('''CREATEM
 INDEX IF NOT EXISTS
                      addresses_idx ON addresses (address)
                   ''')
    send.initialise(db)
    destroy.initialise(db)
    order.initialise(db)
    btcpay.initialise(db)
    issuance.initialise(db)
    broadcast.initialise(db)
    bet.initialise(db)
    dividend.initialise(db)
    burn.initialise(db)
    cancel.initialise(db)
    rps.initialise(db)
    rpsresolve.initialise(db)
    sweep.initialise(db)
    dispenser.initialise(db)
ecute('''CREATE TABLE IF NOT EXISTS messages(
                      message_index INTEGER PRIMARY KEY,
                      block_index INTEGER,
                      command TEXT,
                      category TEXT,
                      bindings TEXT,
                      timestamp INTEGER)
                  ''')
                      # TODO: FOREIGN KEY (block_index) REFERENCES blocks(block_index) DEFERRABLE INITIALLY DEFERRED)
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_indexM
_idx ON messages (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_message_index_idx ON messages (block_index, message_index)
                   ''')
    # Create undolog tables
    cursor.execute('''CREATE TABLE IF NOT EXISTS undolog(
                        undo_index INTEGER PRIMARY KEY AUTOINCREMENT,
                        sql TEXT)
                   ''')
    cursor.execute('''CREATE TABLE IF NOT EXISTS undolog_block(
                    M
    block_index INTEGER PRIMARY KEY,
                        first_undo_index INTEGER)
                   ''')
    # Create undolog triggers for all tables in TABLES list, plus the 'balances' table
    for table in UNDOLOG_TABLES:
        columns = [column['name'] for column in cursor.execute('''PRAGMA table_info({})'''.format(table))]
        cursor.execute('''CREATE TRIGGER IF NOT EXISTS _{}_insert AFTER INSERT ON {} BEGIN
                            INSERT INTO undolog VALUES(NULL, 'DELETE FROM {} WHERE rowid='|M
                            END;
                       '''.format(table, table, table))
        columns_parts = ["{}='||quote(old.{})||'".format(c, c) for c in columns]
        cursor.execute('''CREATE TRIGGER IF NOT EXISTS _{}_update AFTER UPDATE ON {} BEGIN
                            INSERT INTO undolog VALUES(NULL, 'UPDATE {} SET {} WHERE rowid='||old.rowid);
                            END;
                       '''.format(table, table, table, ','.join(columns_parts)))
        columns_parts = M
["'||quote(old.{})||'".format(c) for c in columns]
        cursor.execute('''CREATE TRIGGER IF NOT EXISTS _{}_delete BEFORE DELETE ON {} BEGIN
                            INSERT INTO undolog VALUES(NULL, 'INSERT INTO {}(rowid,{}) VALUES('||old.rowid||',{})');
                            END;
                       '''.format(table, table, table, ','.join(columns), ','.join(columns_parts)))
    # Drop undolog tables on messages table if they exist (fix for adding them in 9.52.0)
    for trigger_type in ('insert', 'uM
        cursor.execute("DROP TRIGGER IF EXISTS _messages_{}".format(trigger_type))
    # Mempool messages
    # NOTE: `status`, 'block_index` are removed from bindings.
    cursor.execute('''DROP TABLE IF EXISTS mempool''')
    cursor.execute('''CREATE TABLE mempool(
                      tx_hash TEXT,
                      command TEXT,
                      category TEXT,
                      bindings TEXT,
                      timestamp INTEGER)
                  ''')
ef get_tx_info(tx_hex, block_parser=None, block_index=None, db=None):
    """Get the transaction info. Returns normalized None data for DecodeError and BTCOnlyError."""
        return _get_tx_info(tx_hex, block_parser, block_index)
    except DecodeError as e:
        return b'', None, None, None, None, None
    except BTCOnlyError as e:
        # NOTE: For debugging, logger.debug('Could not decode: ' + str(e))
        if util.enabled('dispensers', block_index):
                return b'',M
 None, None, None, None, _get_swap_tx(e.decodedTx, block_parser, block_index, db=db)
            except: # (DecodeError, backend.indexd.BackendRPCError) as e:
                return b'', None, None, None, None, None
            return b'', None, None, None, None, None
def _get_swap_tx(decoded_tx, block_parser=None, block_index=None, db=None):
    def get_pubkeyhash(scriptpubkey):
        asm = script.get_asm(scriptpubkey)
        if len(asm) > 0:
            if asm[0] == "OP_DUP":
      if len(asm) != 5 or asm[1] != 'OP_HASH160' or asm[3] != 'OP_EQUALVERIFY' or asm[4] != 'OP_CHECKSIG':
                    return False
                else:
                    return {"pubkeyhash":asm[2],"address_version":config.ADDRESSVERSION}
            elif (asm[0] == "OP_HASH160") and util.enabled('p2sh_dispensers_support'):
                if len(asm) != 3 or asm[-1] != 'OP_EQUAL':
                    return False
                else:
                    return {"pubkeyhash":asm[1M
],"address_version":config.P2SH_ADDRESSVERSION}
        return False
    def get_address(scriptpubkey):
        if util.enabled('correct_segwit_txids') and scriptpubkey.is_witness_v0_keyhash():
            pubkey = scriptpubkey[2:]
            address = str(bitcoinlib.bech32.CBech32Data.from_bytes(0, pubkey))
            return address
            pubkeyhashdict = get_pubkeyhash(scriptpubkey)
            if not pubkeyhashdict:
                return False
            pubkeyhash = pubM
keyhashdict["pubkeyhash"]
            address_version = pubkeyhashdict["address_version"]
            pubkeyhash = binascii.hexlify(pubkeyhash).decode('utf-8')
            address = script.base58_check_encode(pubkeyhash, address_version)
            # Test decoding of address.
            if address != config.UNSPENDABLE and binascii.unhexlify(bytes(pubkeyhash, 'utf-8')) != script.base58_check_decode(address, address_version):
                return False
            return address
   check_sources = db == None # If we didn't get passed a database cursor, assume we have to check for dispenser
    for vout in decoded_tx.vout:
        address = get_address(vout.scriptPubKey)
        destination = None
        btc_amount = None
            destination = address
            btc_amount = vout.nValue
        elif util.enabled('hotfix_dispensers_with_non_p2pkh'):
            asm = script.get_asm(vout.scriptPubKey)
            if asm[-1] == 'OP_CHECKSIG':
                destinatiM
on, new_data = decode_checksig(asm, decoded_tx)
            elif asm[-1] == 'OP_CHECKMULTISIG':
                destination, new_data = decode_checkmultisig(asm, decoded_tx)
            elif asm[0] == 'OP_HASH160' and asm[-1] == 'OP_EQUAL' and len(asm) == 3:
                destination, new_data = decode_scripthash(asm)
            elif asm[0] == 'OP_RETURN':
                pass #Just ignore.
            elif util.enabled('segwit_support') and asm[0] == 0:
                # Segwit output
                destinatioM
n, new_data = decode_p2w(vout.scriptPubKey)
                logger.error('unrecognised scriptPubkey. Just ignore this: ' + str(asm))
            if destination and not new_data:
                amount = vout.nValue
                logger.error('cannot parse destination address or new_data found: ' + str(asm))
        if db != None and dispenser.is_dispensable(db, destination, btc_amount):
            check_sources = True
            outputs.append((destination, btc_amount))
  # Collect all (unique) source addresses.
    #   if we haven't found them yet
    if check_sources:
        for vin in decoded_tx.vin[:]:                   # Loop through inputs.
            if block_parser:
                vin_tx = block_parser.read_raw_transaction(ib2h(vin.prevout.hash))
                vin_ctx = backend.deserialize(vin_tx['__data__'])
                vin_tx = backend.getrawtransaction(ib2h(vin.prevout.hash)) # TODO: Biggest penalty on parsing is here
        vin_ctx = backend.deserialize(vin_tx)
            vout = vin_ctx.vout[vin.prevout.n]
            asm = script.get_asm(vout.scriptPubKey)
            if asm[-1] == 'OP_CHECKSIG':
                new_source, new_data = decode_checksig(asm, decoded_tx)
                if new_data or not new_source:
                    raise DecodeError('data in source')
            elif asm[-1] == 'OP_CHECKMULTISIG':
                new_source, new_data = decode_checkmultisig(asm, decoded_tx)
                if new_data or noM
                    raise DecodeError('data in source')
            elif asm[0] == 'OP_HASH160' and asm[-1] == 'OP_EQUAL' and len(asm) == 3:
                new_source, new_data = decode_scripthash(asm)
                if new_data or not new_source:
                    raise DecodeError('data in source')
            elif util.enabled('segwit_support') and asm[0] == 0:
                # Segwit output
                # Get the full transaction data for this input transaction.
                new_source,M
 new_data = decode_p2w(vout.scriptPubKey)
                raise DecodeError('unrecognised source type')
            # old; append to sources, results in invalid addresses
            # new; first found source is source, the rest can be anything (to fund the TX for example)
            if not (util.enabled('first_input_is_source') and len(sources)):
                # Collect unique sources.
                if new_source not in sources:
                    sources.append(new_source)
def _get_tx_info(tx_hex, block_parser=None, block_index=None, p2sh_is_segwit=False):
    """Get the transaction info. Calls one of two subfunctions depending on signature type."""
    if not block_index:
        block_index = util.CURRENT_BLOCK_INDEX
    if util.enabled('p2sh_addresses', block_index=block_index):   # Protocol change.
        return  get_tx_info3(tx_hex, block_parser=block_parser, p2sh_is_segwit=p2sh_is_segwit)
    elif util.enabled('multisig_addresses', block_index=block_index):   M
        return get_tx_info2(tx_hex, block_parser=block_parser)
        return get_tx_info1(tx_hex, block_index, block_parser=block_parser)
def get_tx_info1(tx_hex, block_index, block_parser=None):
    """Get singlesig transaction info.
    The destination, if it exists, always comes before the data output; the
    change, if it exists, always comes after.
    ctx = backend.deserialize(tx_hex)
    def get_pubkeyhash(scriptpubkey):
        asm = script.get_asm(scriptpubkey)
    if len(asm) != 5 or asm[0] != 'OP_DUP' or asm[1] != 'OP_HASH160' or asm[3] != 'OP_EQUALVERIFY' or asm[4] != 'OP_CHECKSIG':
            return False
        return asm[2]
    def get_address(scriptpubkey):
        pubkeyhash = get_pubkeyhash(scriptpubkey)
        if not pubkeyhash:
            return False
        pubkeyhash = binascii.hexlify(pubkeyhash).decode('utf-8')
        address = script.base58_check_encode(pubkeyhash, config.ADDRESSVERSION)
        # Test decoding of address.
        if address != confM
ig.UNSPENDABLE and binascii.unhexlify(bytes(pubkeyhash, 'utf-8')) != script.base58_check_decode(address, config.ADDRESSVERSION):
            return False
        return address
    # Fee is the input values minus output values.
    # Get destination output and data output.
    destination, btc_amount, data = None, None, b''
    pubkeyhash_encoding = False
    for vout in ctx.vout:
        fee -= vout.nValue
        # Sum data chunks to get data. (Can mix OP_RETURN and multi-sig.)
        asm = scripM
t.get_asm(vout.scriptPubKey)
        if len(asm) == 2 and asm[0] == 'OP_RETURN':                                             # OP_RETURN
            if type(asm[1]) != bytes:
                continue
            data_chunk = asm[1]
            data += data_chunk
        elif len(asm) == 5 and asm[0] == 1 and asm[3] == 2 and asm[4] == 'OP_CHECKMULTISIG':    # Multi-sig
            if type(asm[2]) != bytes:
                continue
            data_pubkey = asm[2]
            data_chunk_length = data_pubkey[0]  # No M
            data_chunk = data_pubkey[1:data_chunk_length + 1]
            data += data_chunk
        elif len(asm) == 5 and (block_index >= 293000 or config.TESTNET or config.REGTEST):    # Protocol change.
            # Be strict.
            pubkeyhash = get_pubkeyhash(vout.scriptPubKey)
            if not pubkeyhash:
                continue
            if ctx.is_coinbase():
                raise DecodeError('coinbase transaction')
            obj1 = arc4.init_arc4(ctx.vin[0].prevout.hash[::-1]M
            data_pubkey = obj1.decrypt(pubkeyhash)
            if data_pubkey[1:9] == config.PREFIX or pubkeyhash_encoding:
                pubkeyhash_encoding = True
                data_chunk_length = data_pubkey[0]  # No ord() necessary.
                data_chunk = data_pubkey[1:data_chunk_length + 1]
                if data_chunk[-8:] == config.PREFIX:
                    data += data_chunk[:-8]
                    break
                else:
                    data += data_chunk
        # Destination is tM
he first output before the data.
        if not destination and not btc_amount and not data:
            address = get_address(vout.scriptPubKey)
            if address:
                destination = address
                btc_amount = vout.nValue
    # Check for, and strip away, prefix (except for burns).
    if destination == config.UNSPENDABLE:
    elif data[:len(config.PREFIX)] == config.PREFIX:
        data = data[len(config.PREFIX):]
        raise DecodeError('no prefix')
look for source if data were found or destination is UNSPENDABLE, for speed.
    if not data and destination != config.UNSPENDABLE:
        raise BTCOnlyError('no data and not unspendable')
    # Collect all possible source addresses; ignore coinbase transactions and anything but the simplest Pay
    source_list = []
    for vin in ctx.vin[:]:                                               # Loop through input transactions.
        if vin.prevout.is_null():
            raise DecodeError('cM
oinbase transaction')
         # Get the full transaction data for this input transaction.
        if block_parser:
            vin_tx = block_parser.read_raw_transaction(ib2h(vin.prevout.hash))
            vin_ctx = backend.deserialize(vin_tx['__data__'])
            vin_tx = backend.getrawtransaction(ib2h(vin.prevout.hash))
            vin_ctx = backend.deserialize(vin_tx)
        vout = vin_ctx.vout[vin.prevout.n]
        fee += vout.nValue
        address = get_address(vout.scriptPubKey)
            raise DecodeError('invalid scriptpubkey')
            source_list.append(address)
    # Require that all possible source addresses be the same.
    if all(x == source_list[0] for x in source_list):
        source = source_list[0]
        source = None
    return source, destination, btc_amount, fee, data, None
def get_tx_info3(tx_hex, block_parser=None, p2sh_is_segwit=False):
    return get_tx_info2(tx_hex, block_parser=block_parser, p2sh_support=True, p2sh_is_M
segwit=p2sh_is_segwit)
def arc4_decrypt(cyphertext, ctx):
obfuscate. Initialise key once per attempt.'''
    key = arc4.init_arc4(ctx.vin[0].prevout.hash[::-1])
    return key.decrypt(cyphertext)
def get_opreturn(asm):
    if len(asm) == 2 and asm[0] == 'OP_RETURN':
        pubkeyhash = asm[1]
        if type(pubkeyhash) == bytes:
            return pubkeyhash
    raise DecodeError('invalid OP_RETURN')
def decode_opreturn(asm, ctx):
    chunk = get_opreturn(asm)
    chunk = arc4_decrypt(chunk, ctx)
   if chunk[:len(config.PREFIX)] == config.PREFIX:             # Data
        destination, data = None, chunk[len(config.PREFIX):]
        raise DecodeError('unrecognised OP_RETURN output')
    return destination, data
def decode_checksig(asm, ctx):
    pubkeyhash = script.get_checksig(asm)
    chunk = arc4_decrypt(pubkeyhash, ctx)
    if chunk[1:len(config.PREFIX) + 1] == config.PREFIX:        # Data
        # Padding byte in each output (instead of just in the last one) so that encoding methods may beM
s just not very much data.
        chunk_length = chunk[0]
        chunk = chunk[1:chunk_length + 1]
        destination, data = None, chunk[len(config.PREFIX):]
    else:                                                       # Destination
        pubkeyhash = binascii.hexlify(pubkeyhash).decode('utf-8')
        destination, data = script.base58_check_encode(pubkeyhash, config.ADDRESSVERSION), None
    return destination, data
def decode_scripthash(asm):
    destination = script.base58_check_enM
code(binascii.hexlify(asm[1]).decode('utf-8'), config.P2SH_ADDRESSVERSION)
    return destination, None
def decode_checkmultisig(asm, ctx):
    pubkeys, signatures_required = script.get_checkmultisig(asm)
    for pubkey in pubkeys[:-1]:     # (No data in last pubkey.)
        chunk += pubkey[1:-1]       # Skip sign byte and nonce byte.
    chunk = arc4_decrypt(chunk, ctx)
    if chunk[1:len(config.PREFIX) + 1] == config.PREFIX:        # Data
        # Padding byte in each output (instead of just iM
n the last one) so that encoding methods may be mixed. Also, it
s just not very much data.
        chunk_length = chunk[0]
        chunk = chunk[1:chunk_length + 1]
        destination, data = None, chunk[len(config.PREFIX):]
    else:                                                       # Destination
        pubkeyhashes = [script.pubkey_to_pubkeyhash(pubkey) for pubkey in pubkeys]
        destination, data = script.construct_array(signatures_required, pubkeyhashes, len(pubkeyhashes)), None
    return destinatM
def decode_p2w(script_pubkey):
        bech32 = bitcoinlib.bech32.CBech32Data.from_bytes(0, script_pubkey[2:22])
        return str(bech32), None
    except TypeError as e:
        raise DecodeError('bech32 decoding error')
def get_tx_info2(tx_hex, block_parser=None, p2sh_support=False, p2sh_is_segwit=False):
    """Get multisig transaction info.
    The destinations, if they exists, always comes before the data output; the
    change, if it exists, always comes after.
    ctx = backend.deserialize(tx_hex)
    # Ignore coinbase transactions.
    if ctx.is_coinbase():
        raise DecodeError('coinbase transaction')
    # Get destinations and data outputs.
    destinations, btc_amount, fee, data = [], 0, 0, b''
    for vout in ctx.vout:
        # Fee is the input values minus output values.
        output_value = vout.nValue
        fee -= output_value
        # Ignore transactions with invalid script.
            asm = script.get_asm(vout.scriptPM
        except CScriptInvalidError as e:
            raise DecodeError(e)
        if asm[0] == 'OP_RETURN':
            new_destination, new_data = decode_opreturn(asm, ctx)
        elif asm[-1] == 'OP_CHECKSIG':
            new_destination, new_data = decode_checksig(asm, ctx)
        elif asm[-1] == 'OP_CHECKMULTISIG':
                new_destination, new_data = decode_checkmultisig(asm, ctx)
                raise DecodeError('unrecognised output type')
sh_support and asm[0] == 'OP_HASH160' and asm[-1] == 'OP_EQUAL' and len(asm) == 3:
            new_destination, new_data = decode_scripthash(asm)
        elif util.enabled('segwit_support') and asm[0] == 0:
            # Segwit Vout, second param is redeemScript
            #redeemScript = asm[1]
            new_destination, new_data = decode_p2w(vout.scriptPubKey)
            raise DecodeError('unrecognised output type')
        assert not (new_destination and new_data)
        assert new_destinationM
 != None or new_data != None  # `decode_*()` should never return `None, None`.
        if util.enabled('null_data_check'):
            if new_data == []:
                raise DecodeError('new destination is `None`')
        # All destinations come before all data.
        if not data and not new_data and destinations != [config.UNSPENDABLE,]:
            destinations.append(new_destination)
            btc_amount += output_value
            if new_destination:     # Change.
                break
          else:                   # Data.
                data += new_data
    # source can be determined by parsing the p2sh_data transaction
    #   or from the first spent output
    # P2SH encoding signalling
    p2sh_encoding_source = None
    if util.enabled('p2sh_encoding') and data == b'P2SH':
        for vin in ctx.vin:
            if util.enabled("prevout_segwit_fix"):
                vin_tx = backend.getrawtransaction(ib2h(vin.prevout.hash))
                vin_ctx =M
 backend.deserialize(vin_tx)
                prevout_is_segwit = vin_ctx.has_witness()
                prevout_is_segwit = p2sh_is_segwit
            # Ignore transactions with invalid script.
                asm = script.get_asm(vin.scriptSig)
            except CScriptInvalidError as e:
                raise DecodeError(e)
            new_source, new_destination, new_data = p2sh_encoding.decode_p2sh_input(asm, p2sh_is_segwit=prevout_is_segwit)
s could be a p2sh source address with no encoded data
            if new_data is None:
              continue;
            if new_source is not None:
                if p2sh_encoding_source is not None and new_source != p2sh_encoding_source:
                    # this p2sh data input has a bad source address
                    raise DecodeError('inconsistent p2sh inputs')
                p2sh_encoding_source = new_source
            assert not new_destination
            data += new_data
    # Only look for soM
urce if data were found or destination is `UNSPENDABLE`,
    if not data and destinations != [config.UNSPENDABLE,]:
        raise BTCOnlyError('no data and not unspendable', ctx)
    # Collect all (unique) source addresses.
    #   if we haven't found them yet
    for vin in ctx.vin[:]:                   # Loop through inputs.
        # Get the full transaction data for this input transaction.
        if block_parser:
            vin_tx = block_parser.read_raw_transaction(ib2h(vin.prevout.hash))
           vin_ctx = backend.deserialize(vin_tx['__data__'])
            vin_tx = backend.getrawtransaction(ib2h(vin.prevout.hash))
            vin_ctx = backend.deserialize(vin_tx)
        vout = vin_ctx.vout[vin.prevout.n]
        fee += vout.nValue
        asm = script.get_asm(vout.scriptPubKey)
        if asm[-1] == 'OP_CHECKSIG':
            new_source, new_data = decode_checksig(asm, ctx)
            if new_data or not new_source:
                raise DecodeError('data in source')
 asm[-1] == 'OP_CHECKMULTISIG':
            new_source, new_data = decode_checkmultisig(asm, ctx)
            if new_data or not new_source:
                raise DecodeError('data in source')
        elif p2sh_support and asm[0] == 'OP_HASH160' and asm[-1] == 'OP_EQUAL' and len(asm) == 3:
            new_source, new_data = decode_scripthash(asm)
            if new_data or not new_source:
                raise DecodeError('data in source')
        elif util.enabled('segwit_support') and asm[0] == 0:
            new_source, new_data = decode_p2w(vout.scriptPubKey)
            raise DecodeError('unrecognised source type')
        # old; append to sources, results in invalid addresses
        # new; first found source is source, the rest can be anything (to fund the TX for example)
        if not (util.enabled('first_input_is_source') and len(sources)):
            # Collect unique sources.
            if new_source not in sources:
                sources.append(new_source)
the source from the p2sh data source
    if p2sh_encoding_source is not None:
        sources = p2sh_encoding_source
        sources = '-'.join(sources)
    destinations = '-'.join(destinations)
    return sources, destinations, btc_amount, round(fee), data, None
def reinitialise(db, block_index=None):
    """Drop all predefined tables and initialise the database once again."""
    cursor = db.cursor()
    # Delete all of the results of parsing (including the undolog)
    for table in TABLES + ['balancM
es', 'undolog', 'undolog_block']:
        cursor.execute('''DROP TABLE IF EXISTS {}'''.format(table))
    # Create missing tables
    # clean consensus hashes if first block hash doesn't match with checkpoint.
    if config.TESTNET:
        checkpoints = check.CHECKPOINTS_TESTNET
    elif config.REGTEST:
        checkpoints = check.CHECKPOINTS_REGTEST
        checkpoints = check.CHECKPOINTS_MAINNET
    columns = [column['name'] for column in cursor.execute('''PRAGMA table_info(blocksM
    for field in ['ledger_hash', 'txlist_hash']:
        if field in columns:
            sql = '''SELECT {} FROM blocks  WHERE block_index = ?'''.format(field)
            first_block = list(cursor.execute(sql, (config.BLOCK_FIRST,)))
            if first_block:
                first_hash = first_block[0][field]
                if first_hash != checkpoints[config.BLOCK_FIRST][field]:
                    logger.info('First hash changed. Cleaning {}.'.format(field))
                    cursor.execute('''UPDATM
E blocks SET {} = NULL'''.format(field))
    # For rollbacks, just delete new blocks and then reparse what
        cursor.execute('''DELETE FROM transactions WHERE block_index > ?''', (block_index,))
        cursor.execute('''DELETE FROM blocks WHERE block_index > ?''', (block_index,))
    elif config.TESTNET or config.REGTEST:  # block_index NOT specified and we are running testnet
        # just blow away the consensus hashes with a full testnet reparse, as we could activate
 # new features retroactively, which could otherwise lead to ConsensusError exceptions being raised.
        logger.info("Testnet/regtest full reparse detected: Clearing all consensus hashes before performing reparse.")
        cursor.execute('''UPDATE blocks SET ledger_hash = NULL, txlist_hash = NULL, messages_hash = NULL''')
def reparse(db, block_index=None, quiet=False):
    """Reparse all transactions (atomically). If block_index is set, rollback
    to the end of that block.
f reparse_from_undolog(db, block_index, quiet):
        """speedy reparse method that utilizes the undolog.
        if fails, fallback to the full reparse method"""
        if not block_index:
            return False # Can't reparse from undolog
        undolog_cursor = db.cursor()
        undolog_cursor.setexectrace(None)
        undolog_cursor.setrowtrace(None)
        def get_block_index_for_undo_index(undo_indexes, undo_index):
            for block_index, first_undo_index in undo_indexes.items(): #in order
                if undo_index < first_undo_index:
                    return block_index - 1
                return next(reversed(undo_indexes)) #the last inserted block_index
            # Check if we can reparse from the undolog
            results = list(undolog_cursor.execute(
                '''SELECT block_index, first_undo_index FROM undolog_block WHERE block_index >= ? ORDER BY block_index ASC''', (block_index,)))
            undo_indexes = collections.OrderedDict()
       for result in results:
                undo_indexes[result[0]] = result[1]
            undo_start_block_index = block_index + 1
            if undo_start_block_index not in undo_indexes:
                if block_index in undo_indexes:
                    # Edge case, should only happen if we're "rolling back" to latest block (e.g. via cmd line)
                    return True #skip undo
                else:
                    return False # Undolog doesn't go that far back, full reparse required...
        # Grab the undolog...
            undolog = list(undolog_cursor.execute(
                '''SELECT undo_index, sql FROM undolog WHERE undo_index >= ? ORDER BY undo_index DESC''',
                (undo_indexes[undo_start_block_index],)))
            # Replay the undolog backwards, from the last entry to first_undo_index...
            for entry in undolog:
                logger.info("Undolog: Block {} (undo_index {}): {}".format(
                    get_block_index_for_undo_index(undo_indexes, entry[0]), eM
                undolog_cursor.execute(entry[1])
            # Trim back tx and blocks
            undolog_cursor.execute('''DELETE FROM transactions WHERE block_index > ?''', (block_index,))
            undolog_cursor.execute('''DELETE FROM blocks WHERE block_index > ?''', (block_index,))
            # As well as undolog entries...
            undolog_cursor.execute('''DELETE FROM undolog WHERE undo_index >= ?''', (undo_indexes[undo_start_block_index],))
            undolog_cursor.execute('''DM
ELETE FROM undolog_block WHERE block_index >= ?''', (undo_start_block_index,))
        undolog_cursor.close()
        logger.info('Rolling back transactions to block {}.'.format(block_index))
        logger.info('Reparsing all transactions.')
    check.software_version()
    reparse_start = time.time()
    # Reparse from the undolog if possible
    reparsed = reparse_from_undolog(db, block_index, quiet)
    cursor = db.cursor()
    if not reparsed:
            logger.info("Could not roll back from undolog. Performing full reparse instead...")
            root_logger = logging.getLogger()
            root_level = logger.getEffectiveLevel()
            reinitialise(db, block_index)
            # Reparse all blocks, transactions.
            if quiet:
                root_logger.setLevel(logging.WARNING)
            previous_ledger_hash, previous_txlist_hash, previous_messages_hash = None, None, None
  cursor.execute('''SELECT * FROM blocks ORDER BY block_index''')
            for block in cursor.fetchall():
                util.CURRENT_BLOCK_INDEX = block['block_index']
                previous_ledger_hash, previous_txlist_hash, previous_messages_hash, previous_found_messages_hash = parse_block(
                                                                         db, block['block_index'], block['block_time'],
                                                                         previous_ledger_hash=prevM
                                                                         previous_txlist_hash=previous_txlist_hash,
                                                                         previous_messages_hash=previous_messages_hash)
                if quiet and block['block_index'] % 10 == 0:  # every 10 blocks print status
                    root_logger.setLevel(logging.INFO)
                logger.info('Block (re-parse): %s (hashes: L:%s / TX:%s / M:%s%s)' % (
                    block['blocM
k_index'], previous_ledger_hash[-5:], previous_txlist_hash[-5:], previous_messages_hash[-5:],
                    (' [overwrote %s]' % previous_found_messages_hash) if previous_found_messages_hash and previous_found_messages_hash != previous_messages_hash else ''))
                if quiet and block['block_index'] % 10 == 0:
                    root_logger.setLevel(logging.WARNING)
            root_logger.setLevel(root_level)
        # Check for conservation of assets.
.asset_conservation(db)
        # Update database version number.
        database.update_version(db)
    reparse_end = time.time()
    logger.info("Reparse took {:.3f} minutes.".format((reparse_end - reparse_start) / 60.0))
    # on full reparse - vacuum the DB afterwards for better subsequent performance (especially on non-SSDs)
    if not block_index:
        database.vacuum(db)
def list_tx(db, block_hash, block_index, block_time, tx_hash, tx_index, tx_hex=None):
    assert type(tx_hash) =M
    cursor = db.cursor()
    # Edge case: confirmed tx_hash also in mempool
    cursor.execute('''SELECT * FROM transactions WHERE tx_hash = ?''', (tx_hash,))
    transactions = list(cursor)
    if transactions:
        return tx_index
    # Get the important details about each transaction.
    if tx_hex is None:
        tx_hex = backend.getrawtransaction(tx_hash) # TODO: This is the call that is stalling the process the most
    source, destination, btc_amount, fee, data, decoded_tx = get_tx_info(tx_hex, M
    if not source and decoded_tx and util.enabled('dispensers', block_index):
        outputs = decoded_tx[1]
        for out in outputs:
            if out[0] != decoded_tx[0][0] and dispenser.is_dispensable(db, out[0], out[1]):
                source = decoded_tx[0][0]
                destination = out[0]
                btc_amount = out[1]
                fee = 0
                data = struct.pack(config.SHORT_TXTYPE_FORMAT, dispenser.DISPENSE_ID)
                data += b'\x00'
                break # PM
revent inspection of further dispenses (only first one is valid)
    if block_hash == None:
        block_hash = config.MEMPOOL_BLOCK_HASH
        block_index = config.MEMPOOL_BLOCK_INDEX
        assert block_index == util.CURRENT_BLOCK_INDEX
    if source and (data or destination == config.UNSPENDABLE or decoded_tx):
        logger.debug('Saving transaction: {}'.format(tx_hash))
        cursor.execute('''INSERT INTO transactions(
                            tx_index,
                            block_index,
                            block_hash,
                            block_time,
                            source,
                            destination,
                            btc_amount,
                            fee,
                            data) VALUES(?,?,?,?,?,?,?,?,?,?)''',
                            (tx_index,
                             tx_hash,
                             block_index,
                             block_hash,
                          block_time,
                             source,
                             destination,
                             btc_amount,
                             fee,
                             data)
                      )
        cursor.close()
        return tx_index + 1
        logger.getChild('list_tx.skip').debug('Skipping transaction: {}'.format(tx_hash))
def kickstart(db, bitcoind_dir):
    if bitcoind_dir is None:
        if platform.system() == 'DM
            bitcoind_dir = os.path.expanduser('~/Library/Application Support/Bitcoin/')
        elif platform.system() == 'Windows':
            bitcoind_dir = os.path.join(os.environ['APPDATA'], 'Bitcoin')
            bitcoind_dir = os.path.expanduser('~/.bitcoin')
    if not os.path.isdir(bitcoind_dir):
        raise Exception('Bitcoin Core data directory not found at {}. Use --bitcoind-dir parameter.'.format(bitcoind_dir))
    cursor = db.cursor()
    logger.warning('''Warning:
hat bitcoind is stopped.
- You must reindex bitcoind after the initialization is complete (restart with `-reindex=1`)
- The initialization may take a while.''')
    if input('Proceed with the initialization? (y/N) : ') != 'y':
    if config.TESTNET:
        first_hash = config.BLOCK_FIRST_TESTNET_HASH
    elif config.REGTEST:
        first_hash = config.BLOCK_FIRST_REGTEST_HASH
        first_hash = config.BLOCK_FIRST_MAINNET_HASH
    start_time_total = time.time()
    # Get hash of last M
    chain_parser = ChainstateParser(os.path.join(bitcoind_dir, 'chainstate'))
    last_hash = chain_parser.get_last_block_hash()
    chain_parser.close()
    # Start block parser.
    block_parser = BlockchainParser(os.path.join(bitcoind_dir, 'blocks'), os.path.join(bitcoind_dir, 'blocks/index'))
    current_hash = last_hash
        # Prepare SQLite database. # TODO: Be more specific!
        logger.info('Preparing database.')
        start_time = time.time()
nitialise(db, block_index=config.BLOCK_FIRST - 1)
        logger.info('Prepared database in {:.3f}s'.format(time.time() - start_time))
        # Get blocks and transactions, moving backwards in time.
        while current_hash != None:
            start_time = time.time()
            transactions = []
            # Get `tx_info`s for transactions in this block.
            block = block_parser.read_raw_block(current_hash)
            for tx in block['transactions']:
                source, destination, btc_amountM
, fee, data = get_tx_info(tx['__data__'], block_parser=block_parser, block_index=block['block_index'])
                if source and (data or destination == config.UNSPENDABLE):
                    transactions.append((
                        tx['tx_hash'], block['block_index'], block['block_hash'], block['block_time'],
                        source, destination, btc_amount, fee, data
                    ))
                    logger.info('Valid transaction: {}'.format(tx['tx_hash']))
            # Insert block M
and transactions into database.
            cursor.execute('''INSERT INTO blocks(
                                    block_index,
                                    block_hash,
                                    block_time) VALUES(?,?,?)''',
                                    (block['block_index'],
                                    block['block_hash'],
                                    block['block_time']))
            if len(transactions):
                transactions = list(reversed(transactions))
         tx_chunks = [transactions[i:i+90] for i in range(0, len(transactions), 90)]
                for tx_chunk in tx_chunks:
                    sql = '''INSERT INTO transactions
                                (tx_index, tx_hash, block_index, block_hash, block_time, source, destination, btc_amount, fee, data)
                             VALUES '''
                    bindings = ()
                    bindings_place = []
                    # negative tx_index from -1 and inverse order for fast reordering   # TM
ODO: Can this be clearer?
                    for tx in tx_chunk:
                        bindings += (-(tx_index + 1),) + tx
                        bindings_place.append('''(?,?,?,?,?,?,?,?,?,?)''')
                        tx_index += 1
                    sql += ', '.join(bindings_place)
                    cursor.execute(sql, bindings)
            logger.info('Block {} ({}): {}/{} saved in {:.3f}s'.format(
                          block['block_index'], block['block_hash'],
                          len(transaM
ctions), len(block['transactions']),
                          time.time() - start_time))
            # Get hash of next block.
            current_hash = block['hash_prev'] if current_hash != first_hash else None
        block_parser.close()
        # Reorder all transactions in database.
        logger.info('Reordering transactions.')
        start_time = time.time()
        cursor.execute('''UPDATE transactions SET tx_index = tx_index + ?''', (tx_index,))
        logger.info('Reordered transactions in {:.3f}sM
.'.format(time.time() - start_time))
    # Parse all transactions in database.
    logger.info('Total duration: {:.3f}s'.format(time.time() - start_time_total))
def last_db_index(db):
    cursor = db.cursor()
        blocks = list(cursor.execute('''SELECT * FROM blocks WHERE block_index = (SELECT MAX(block_index) from blocks)'''))
            return blocks[0]['block_index']
        except IndexError:
            return 0
    except apsw.SQLError:
def get_next_tx_index(db):
    """Return index of next transaction."""
    cursor = db.cursor()
    txes = list(cursor.execute('''SELECT * FROM transactions WHERE tx_index = (SELECT MAX(tx_index) from transactions)'''))
        assert len(txes) == 1
        tx_index = txes[0]['tx_index'] + 1
        tx_index = 0
class MempoolError(Exception):
    # Check software version.
    check.software_version()
    # Get index of last block.
    if util.CURRENT_BLOCK_INDEX == 0:
        logger.warning('New database.')
        block_index = config.BLOCK_FIRST
        block_index = util.CURRENT_BLOCK_INDEX + 1
        # Check database version.
            check.database_version(db)
        except check.DatabaseVersionError as e:
            logger.info(str(e))
            # no need to reparse or rollback a new database
            if block_index != config.BLOCK_FIRST:
      reparse(db, block_index=e.reparse_block_index, quiet=False)
            else: #version update was included in reparse(), so don't do it twice
                database.update_version(db)
    logger.info('Resuming parsing.')
    # Get index of last transaction.
    tx_index = get_next_tx_index(db)
    not_supported = {}   # No false positives. Use a dict to allow for O(1) lookups
    not_supported_sorted = collections.deque()
    # ^ Entries in form of (block_index, tx_hash), oldest first. Allows for easy reM
moval of past, unncessary entries
    cursor = db.cursor()
    # a reorg can happen without the block count increasing, or even for that
    # matter, with the block count decreasing. This should only delay
    # processing of the new blocks a bit.
        start_time = time.time()
        # Get block count.
        # If the backend is unreachable and `config.FORCE` is set, just sleep
        # and try again repeatedly.
            block_count = backend.getblockcount()
onnectionRefusedError, http.client.CannotSendRequest, backend.addrindexrs.BackendRPCError) as e:
            if config.FORCE:
                time.sleep(config.BACKEND_POLL_INTERVAL)
                continue
                raise e
        # Get new blocks.
        if block_index <= block_count:
            current_index = block_index
            # Backwards check for incorrect blocks due to chain reorganisation, and stop when a common parent is found.
            if block_count - block_index < M
100: # Undolog only saves last 100 blocks, if there's a reorg deeper than that manual reparse should be done
                requires_rollback = False
                while True:
                    if current_index == config.BLOCK_FIRST:
                        break
                    logger.debug('Checking that block {} is not an orphan.'.format(current_index))
                    # Backend parent hash.
                    current_hash = backend.getblockhash(current_index)
                    current_cblock = M
backend.getblock(current_hash)
                    backend_parent = bitcoinlib.core.b2lx(current_cblock.hashPrevBlock)
                    # DB parent hash.
                    blocks = list(cursor.execute('''SELECT * FROM blocks
                                                    WHERE block_index = ?''', (current_index - 1,)))
                    if len(blocks) != 1:  # For empty DB.
                        break
                    db_parent = blocks[0]['block_hash']
                    # Compare.
        assert type(db_parent) == str
                    assert type(backend_parent) == str
                    if db_parent == backend_parent:
                        break
                    else:
                        current_index -= 1
                        requires_rollback = True
                # Rollback for reorganisation.
                if requires_rollback:
                    # Record reorganisation.
                    logger.warning('Blockchain reorganisation at block {}.'.format(current_indexM
                    log.message(db, block_index, 'reorg', None, {'block_index': current_index})
                    # Rollback the DB.
                    reparse(db, block_index=current_index-1, quiet=True)
                    block_index = current_index
                    tx_index = get_next_tx_index(db)
                    continue
            # Check version. (Don
t add any blocks to the database while
            # running an out
            check.software_version()
 Get and parse transactions in this block (atomically).
            block_hash = backend.getblockhash(current_index)
            block = backend.getblock(block_hash)
            previous_block_hash = bitcoinlib.core.b2lx(block.hashPrevBlock)
            block_time = block.nTime
            txhash_list, raw_transactions = backend.get_tx_list(block)
            with db:
                util.CURRENT_BLOCK_INDEX = block_index
                # List the block.
                cursor.execute('''INSERT INTO blocks(
                                block_index,
                                    block_hash,
                                    block_time,
                                    previous_block_hash,
                                    difficulty) VALUES(?,?,?,?,?)''',
                                    (block_index,
                                    block_hash,
                                    block_time,
                                    previous_block_hash,
                                    block.difficuM
                              )
                # List the transactions in the block.
                for tx_hash in txhash_list:
                    tx_hex = raw_transactions[tx_hash]
                    tx_index = list_tx(db, block_hash, block_index, block_time, tx_hash, tx_index, tx_hex)
                # Parse the transactions in the block.
                new_ledger_hash, new_txlist_hash, new_messages_hash, found_messages_hash = parse_block(db, block_index, block_time)
            # When newly caught uM
p, check for conservation of assets.
            if block_index == block_count:
                if config.CHECK_ASSET_CONSERVATION:
                    check.asset_conservation(db)
            # Remove any non
supported transactions older than ten blocks.
            while len(not_supported_sorted) and not_supported_sorted[0][0] <= block_index - 10:
                tx_h = not_supported_sorted.popleft()[1]
                del not_supported[tx_h]
            logger.info('Block: %s (%ss, hashes: L:%s / TX:%s / M:%M
                str(block_index), "{:.2f}".format(time.time() - start_time, 3),
                new_ledger_hash[-5:], new_txlist_hash[-5:], new_messages_hash[-5:],
                (' [overwrote %s]' % found_messages_hash) if found_messages_hash and found_messages_hash != new_messages_hash else ''))
            # Increment block index.
            block_count = backend.getblockcount()
            block_index += 1
            # TODO: add zeromq support here to await TXs and Blocks instead ofM
            # Get old mempool.
            old_mempool = list(cursor.execute('''SELECT * FROM mempool'''))
            old_mempool_hashes = [message['tx_hash'] for message in old_mempool]
            if backend.MEMPOOL_CACHE_INITIALIZED is False:
                backend.init_mempool_cache()
                logger.info("Ready for queries.")
            # Fake values for fake block.
            curr_time = int(time.time())
            mempool_tx_index = tx_index
            xcp_mempool = []
         raw_mempool = backend.getrawmempool()
            # this is a quick fix to make counterparty usable on high mempool situations
            # however, this makes the mempool unreliable on counterparty, a better, larger
            # fix must be done by changing this whole function into a zmq driven loop
            if len(raw_mempool) > config.MEMPOOL_TXCOUNT_UPDATE_LIMIT:
                continue
            # For each transaction in Bitcoin Core mempool, if it
            # a fake block,M
 a fake transaction, capture the generated messages,
            # and then save those messages.
            # Every transaction in mempool is parsed independently. (DB is rolled back after each one.)
            # We first filter out which transactions we've already parsed before so we can batch fetch their raw data
            parse_txs = []
            for tx_hash in raw_mempool:
                # If already in mempool, copy to new one.
                if tx_hash in old_mempool_hashes:
                    for meM
ssage in old_mempool:
                        if message['tx_hash'] == tx_hash:
                            xcp_mempool.append((tx_hash, message))
                # If not a supported XCP transaction, skip.
                elif tx_hash in not_supported:
                    pass
                # Else: list, parse and save it.
                else:
                    parse_txs.append(tx_hash)
            # fetch raw for all transactions that need to be parsed
            # Sometimes the transactions can
ound: `{'code': -5, 'message': 'No information available about transaction'}`
            #  - is txindex enabled in Bitcoind?
            #  - or was there a block found while batch feting the raw txs
            #  - or was there a double spend for w/e reason accepted into the mempool (replace-by-fee?)
                raw_transactions = backend.getrawtransaction_batch(parse_txs)
            except Exception as e:
                logger.warning('Failed to fetch raw for mempool TXs, restarting loopM
                continue  # restart the follow loop
            for tx_hash in parse_txs:
                try:
                    with db:
                        # List the fake block.
                        cursor.execute('''INSERT INTO blocks(
                                            block_index,
                                            block_hash,
                                            block_time) VALUES(?,?,?)''',
                                       (config.MEMPOOL_BLOCK_INDEX,
                                       config.MEMPOOL_BLOCK_HASH,
                                        curr_time)
                                      )
                        tx_hex = raw_transactions[tx_hash]
                        if tx_hex is None:
                          logger.debug('tx_hash %s not found in backend.  Not adding to mempool.', (tx_hash, ))
                          raise MempoolError
                        mempool_tx_index = list_tx(db, None, block_index, curr_time, tx_hash, tx_index=M
mempool_tx_index, tx_hex=tx_hex)
                        # Parse transaction.
                        cursor.execute('''SELECT * FROM transactions WHERE tx_hash = ?''', (tx_hash,))
                        transactions = list(cursor)
                        if transactions:
                            assert len(transactions) == 1
                            transaction = transactions[0]
                            supported = parse_tx(db, transaction)
                            if not supported:
               not_supported[tx_hash] = ''
                                not_supported_sorted.append((block_index, tx_hash))
                        else:
                            # If a transaction hasn
                            # table `transactions`, then it
                            # Counterparty transaction.
                            not_supported[tx_hash] = ''
                            not_supported_sorted.append((block_index, tx_hash))
                           M
                        # Save transaction and side
                        cursor.execute('''SELECT * FROM messages WHERE block_index = ?''', (config.MEMPOOL_BLOCK_INDEX,))
                        for message in list(cursor):
                            xcp_mempool.append((tx_hash, message))
                        # Rollback.
                        raise MempoolError
                except exceptions.ParseTransactionError as e:
                    logger.warn('ParseTranM
sactionError for tx %s: %s' % (tx_hash, e))
                except MempoolError:
                    pass
write mempool messages to database.
            with db:
                cursor.execute('''DELETE FROM mempool''')
                for message in xcp_mempool:
                    tx_hash, new_message = message
                    new_message['tx_hash'] = tx_hash
                    cursor.execute('''INSERT INTO mempool VALUES(:tx_hash, :command, :category, :bindings, :timestamp)''', new_messM
            elapsed_time = time.time() - start_time
            sleep_time = config.BACKEND_POLL_INTERVAL - elapsed_time if elapsed_time <= config.BACKEND_POLL_INTERVAL else 0
            logger.getChild('mempool').debug('Refresh mempool: %s XCP txs seen, out of %s total entries (took %ss, next refresh in %ss)' % (
                len(xcp_mempool), len(raw_mempool),
                "{:.2f}".format(elapsed_time, 3),
                "{:.2f}".format(sleep_time, 3)))
            db.wal_checkpM
oint(mode=apsw.SQLITE_CHECKPOINT_PASSIVE)
            time.sleep(sleep_time)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
logger = logging.getLogger(__name__)
from counterpartylib.lib import config
from counterpartylib.lib import util
from counterpartylib.lib import exceptions
from counterpartylib.lib import backend
from counterpartylib.lib import database
CONSENSUS_HASH_SEED = M
'We can only see a short distance ahead, but we can see plenty there that needs to be done.'
CONSENSUS_HASH_VERSION_MAINNET = 2
CHECKPOINTS_MAINNET = {
    config.BLOCK_FIRST_MAINNET: {'ledger_hash': '766ff0a9039521e3628a79fa669477ade241fc4c0ae541c3eae97f34b547b0b7', 'txlist_hash': '766ff0a9039521e3628a79fa669477ade241fc4c0ae541c3eae97f34b547b0b7'},
    280000: {'ledger_hash': '265719e2770d5a6994f6fe49839069183cd842ee14f56c2b870e56641e8a8725', 'txlist_hash': 'a59b33b4633649db4f14586af47e258ed9b8884dbb7aa308fb1f49aM
    290000: {'ledger_hash': '4612ed7034474b4ff1727eb0e216d533ebe7ac755fb015e0f9a170c063f3e84c', 'txlist_hash': 'c15423c849fd360d38cbd6c6c3ea37a07fece723da92353f3056facc2676d9e7'},
    300000: {'ledger_hash': '9a3dd4949780404d61e5ca1929f94a43f08eb0fa19ccb4b5d6a61cafd7943199', 'txlist_hash': 'efa02dbdcc4158a598e3b476ece5ba9cc8d26f3abc8ac3777ac6dde0f0afc7e6'},
    310000: {'ledger_hash': '45e43d5cc77ea01129df01d7f55b0c89b2d4e18cd3d626fd92f30bfb37a85f4d', 'txlist_hash': '83cdcf75833d828ded09979b601fde87e2fM
db0f5eb1cc6ab5d2042b7ec85f90e'},
    320000: {'ledger_hash': '91c1d33626669e8098bc762b1a9e3f616884e4d1cadda4881062c92b0d3d3e98', 'txlist_hash': '761793042d8e7c80e14a16c15bb9d40e237c468a87c207a59730b616bdfde7d4'},
    330000: {'ledger_hash': 'dd56aa97e5ca15841407f383ce1d7814536a594d7cfffcb4cf60bee8b362065a', 'txlist_hash': '3c45b4377a99e020550a198daa45c378c488a72ba199b53deb90b320d55a897b'},
    334000: {'ledger_hash': '24c4fa4097106031267439eb9fbe8ce2a18560169c67726652b608908c1ca9bb', 'txlist_hash': '764ca9e8d3b9546M
d1c4ff441a39594548989f60daefc6f28e046996e76a273bf'},
    335000: {'ledger_hash': 'e57c9d606a615e7e09bf99148596dd28e64b25cd8b081e226d535a64c1ed08d1', 'txlist_hash': '437d9507185b5e193627edf4998aad2264755af8d13dd3948ce119b32dd50ce2'},
    336000: {'ledger_hash': '1329ff5b80d034b64f6ea3481b7c7176437a8837b2a7cb7b8a265fdd1397572d', 'txlist_hash': '33eb8cacd4c750f8132d81e8e43ca13bd565f1734d7d182346364847414da52f'},
    337000: {'ledger_hash': '607e6a93e8d97cefea9bd55384898ee90c8477ded8a46017f2294feedbc83409', 'txlist_hasM
h': '20b535a55abcc902ca70c19dd648cbe5149af8b4a4157b94f41b71fc422d428e'},
    338000: {'ledger_hash': 'f043914c71e4b711abb1c1002767b9a4e7d605e249facaaf7a2046b0e9741204', 'txlist_hash': 'fa2c3f7f76345278271ed5ec391d582858e10b1f154d9b44e5a1f4896400ee46'},
    339000: {'ledger_hash': '49f7240bc90ebc2f242dd599c7d2c427b9d2ac844992131e6e862b638ae4393a', 'txlist_hash': 'c1e3b497c054dcf67ddd0dc223e8b8a6e09a1a05bacb9fef5c03e48bd01e64e7'},
    340000: {'ledger_hash': '255760e2abfb79fdd76b65759f1590f582c1747f3eeccc4b2ae37d23e3M
0e0729', 'txlist_hash': '8502004bb63e699b243ac8af072d704c69b817905e74787c2031af971e8cd87c'},
    341000: {'ledger_hash': '1369cba3909e564d2e725879a8b2cd987df075db121d1d421c8ce16b65f4bf04', 'txlist_hash': 'd217d0bed190cb27f58fcb96b255f8006bc4b9ed739e1bb08507201c49c426c8'},
    342000: {'ledger_hash': '9e7e9b8620717189ccea697ff2f84fe71bc4ae8d991481ff235164d72a9e6e4f', 'txlist_hash': 'adf75d023760101b2b337f6359dd811b12521c83837eb3f7db3bbfd0b095aa54'},
    343000: {'ledger_hash': 'aa47312ebe94b35504bec6c74713e404e5f368M
54e0836839344d13debe50558c', 'txlist_hash': '6bdbbc96364b3c92cea132fe66a0925f9445a249f7062326bdcc4ad4711f0c01'},
    344000: {'ledger_hash': '40187263aa96d1362bf7b19c8ba0fff7f0c0f3eb132a40fc90601b5926c7e6e3', 'txlist_hash': '98da8efe705c4b54275bfd25f816a7e7a4ff1f67647e17d7a0aaa2a3fef8bda0'},
    345000: {'ledger_hash': 'e4a1e1be4beea63d9740ca166b75bb4e3ffa2af33e1fe282e5b09c4952a7448c', 'txlist_hash': '777f163eaa5ad79dcb738871d4318a0699defec469d8afe91ab6277ff8d3e8b8'},
    350000: {'ledger_hash': '6a67e9f2e9d07e7bb3M
277cf9c24f84c857ed1b8fff4a37e589cd56ade276dd95', 'txlist_hash': '96bcbdbce74b782a845d4fda699846d2d3744044c2870a413c018642b8c7c3bf'},
    355000: {'ledger_hash': 'a84b17992217c7845e133a8597dac84eba1ee8c48bcc7f74bcf512837120f463', 'txlist_hash': '210d96b42644432b9e1a3433a29af9acb3bad212b67a7ae1dbc011a11b04bc24'},
    360000: {'ledger_hash': 'ddca07ea43b336b703fb8ebab6c0dc30582eb360d6f0eb0446e1fe58b53dee0a', 'txlist_hash': '31d0ff3e3782cf9464081829c5595b3de5ac477290dc069d98672f3f552767f8'},
    365000: {'ledger_hash':M
 '2d55b126cca3eca15c07b5da683988f9e01d7346d2ca430e940fd7c07ce84fd7', 'txlist_hash': '7988a823cc1e3234953cc87d261d3c1fede8493d0a31b103357eb23cc7dc2eda'},
    366000: {'ledger_hash': '64ce274df2784f9ca88a8d7071613ec6527e506ec31cd434eca64c6a3345a6b7', 'txlist_hash': '0d4374da6100e279b24f4ba4a2d6afbfc4fb0fc2d312330a515806e8c5f49404'},
    370000: {'ledger_hash': 'fabb2a2e91fad3fe7734169d554cca396c1030243044cef42fcf65717cf0fa61', 'txlist_hash': '41d1732868c9ac25951ace5ca9f311a15d5eca9bf8d548e0d988c050bd2aff87'},
000: {'ledger_hash': 'a7ac4e2948cea0c426c8fc201cf57d9c313027ea7bff2b32a25ed28d3dbaa581', 'txlist_hash': '96118a7aa2ca753488755b7419a0f44a7fbc371bc58dcc7ab083c70fc14ef8b3'},
    380000: {'ledger_hash': '70453ba04c1c0198c4771e7964cffa25f9456c2f71456a8b05dfe935d5fcdc88', 'txlist_hash': '8bf2070103cca6f0bde507b7d20b0ba0630da6349beb560fa64c926d08dbcaef'},
    385000: {'ledger_hash': '93eb0a6e820bee197e7591edbc5ead7bfa38f32c88aabf4785f080fd6ae96c4c', 'txlist_hash': '1f8f17fd5766382a8c10a2a0e995a7d5a5d1bcd5fc0220d1e2691b2M
    390000: {'ledger_hash': '7d42b98eecbc910a67a5f4ac8dc7d6d9b6995ebc5bdf53663b414965fe7d2c5e', 'txlist_hash': 'b50efc4a4241bf3ec33a38c3b5f34756a9f305fe5fa9a80f7f9b70d5d7b2a780'},
    395000: {'ledger_hash': '89f9ac390b35e69dd75d6c34854ba501dce2f662fc707aee63cad5822c7660f2', 'txlist_hash': '2151dd2f0aa14685f3d041727a689d5d242578072a049123b317724fc4f1100c'},
    400000: {'ledger_hash': 'eb681a305125e04b6f044b36045e23ee248ce4eb68433cea2b36d15e7e74d5f1', 'txlist_hash': 'b48e9501e8d6f1f1b4127d868860885d3dbM
76698c2c31a567777257df101cf61'},
    405000: {'ledger_hash': '3725055b37a8958ade6ca1c277cf50fee6036b4a92befb8da2f7c32f0b210881', 'txlist_hash': '871b2adfd246e3fe69f0fe9098e3251045ed6e9712c4cf90ea8dfdd1eb330ed6'},
    410000: {'ledger_hash': '1fa9a34f233695ebd7ebb08703bf8d99812fa099f297efc5d307d1ebef902ffd', 'txlist_hash': 'ee3bd84c728a37e2bbe061c1539c9ee6d71db18733b1ed53ee8d320481f55030'},
    415000: {'ledger_hash': '6772a8a1c784db14c0bf111e415919c9da4e5ca142be0b9e323c82c1b13c74e0', 'txlist_hash': 'cfb81785cd48e9bM
a0e54fee4d62f49b347489da82139fd5e1555ae0bc11a33d5'},
    420000: {'ledger_hash': '42167117e16943f44bb8117aa0a39bed2d863a454cd694d0bc5006a7aab23b06', 'txlist_hash': 'a1139870bef8eb9bbe60856029a4f01fce5432eb7aeacd088ba2e033757b86e3'},
CONSENSUS_HASH_VERSION_TESTNET = 7
CHECKPOINTS_TESTNET = {
    config.BLOCK_FIRST_TESTNET: {'ledger_hash': '63f0fef31d02da85fa779e9a0e1b585b1a6a4e59e14564249e288e074e91c223', 'txlist_hash': '63f0fef31d02da85fa779e9a0e1b585b1a6a4e59e14564249e288e074e91c223'},
    316000: {'ledger_hashM
': 'f645e6877da416b8b91670ac927df686c5ea6fc1158c150ae49d594222ed504c', 'txlist_hash': '3e29bcbf3873326097024cc26e9296f0164f552dd79c2ee7cfc344e6d64fa87d'},
    319000: {'ledger_hash': '384ca28ac56976bc24a6ab7572b41bc61474e6b87fdee814135701d6a8f5c8a2', 'txlist_hash': '6c05c98418a6daa6de82dd59e000d3f3f5407c5432d4ab7d76047873a38e4d4b'},
    322000: {'ledger_hash': 'f4015c37eb4f31ac42083fd0389cde4868acb5353d3f3abfe2f3a88aba8cae72', 'txlist_hash': '18f278154e9bc3bbcc39da905ab4ad3023742ab7723b55b0fd1c58c36cd3e9bf'},
25000: {'ledger_hash': 'd7f70a927f5aeed38e559ddc0bc4697601477ea43cde928ad228fefc195b02da', 'txlist_hash': '1a60e38664b39e0f501b3e5a60c6fc0bd4ed311b74872922c2dde4cb2267fd3e'},
    329000: {'ledger_hash': '96637b4400cbe084c2c4f139f59b5bc16770815e96306423aaeb2b2677a9a657', 'txlist_hash': '79d577d8fbba0ad6ae67829dfa5824f758286ccd429d65b7d1d42989134d5b57'},
    350000: {'ledger_hash': 'cae8fec787bba3d5c968a8f4b6fb22a54c96d5acbeadd0425f6b20c3a8813ea3', 'txlist_hash': '097df9c3079df4d96f59518df72492dfd7a79716462e3a4a30d62M
    400000: {'ledger_hash': '94abfd9c00c8462c155f64011e71af141b7d524e17de5aeda26b7469fe79b5f0', 'txlist_hash': 'a9fc42b69f80ec69f3f98e8a3cd81f4f946544fd0561a62a0891254c16970a87'},
    450000: {'ledger_hash': '09eb9f2aa605ce77225362b4b556284acdd9f6d3bc273372dfae4a5be9e9b035', 'txlist_hash': '05af651c1de49d0728834991e50000fbf2286d7928961b71917f682a0f2b7171'},
    500000: {'ledger_hash': '85f3bca8c88246ddfa1a5ec327e71f0696c182ed2a5fedf3712cd2e87e2661ac', 'txlist_hash': '663b34955116a96501e0c1c27f27d24baM
d7d45995913367553c5cfe4b8b9d0a9'},
    550000: {'ledger_hash': 'c143026133af2d83bc49ef205b4623194466ca3e7c79f95da2ad565359ccb5ad', 'txlist_hash': '097b8bca7a243e0b9bdf089f34de15bd2dcd4727fb4e88aae7bfd96302250326'},
    600000: {'ledger_hash': '82caf720967d0e43a1c49a6c75f255d9056ed1bffe3f96d962478faccdaba8ff', 'txlist_hash': '0d99f42184233426d70102d5ac3c80aaecf804d441a8a0d0ef26038d333ab7a7'},
    650000: {'ledger_hash': 'bef100ae7d5027a8b3f32416c4f26e1f16b21cee2a986c57be1466a3ba338051', 'txlist_hash': '409ed86e4274bM
511193d187df92e433c734dcc890bf93496e7a7dee770e7035e'},
    700000: {'ledger_hash': 'afe5e9c3f3a8c6f19c4f9feaf09df051c28202c6bae64f3563a09ffea9e79a6e', 'txlist_hash': '4f9765158855d24950c7e076615b0ad5b72738d4d579decfd3b93c998edf4fcb'},
    750000: {'ledger_hash': 'e7c7969a6156facb193b77ef71b5e3fac49c6998e5a94ec3b90292be10ece9cc', 'txlist_hash': '6e511790656d3ffec0c912d697e5d1c2a4e401a1606203c77ab5a5855891bc2c'},
    800000: {'ledger_hash': '42a7c679e51e5e8d38df26b67673b4850e8e6f72723aa19673b3219fcc02b77b', 'txlist_hM
ash': '885ae1e6c21f5fb3645231aaa6bb6910fc21a0ae0ca5dbe9a4011f3b5295b3e7'},
    850000: {'ledger_hash': '35b2a2ab4a8bfbc321d4545292887b4ccaea73415c7674f795aefa6e240890eb', 'txlist_hash': '72d5cfe1e729a22da9eacd4d7752c881c43a191904556b65a0fae82b770dcdf3'},
    900000: {'ledger_hash': 'a5552b4998d2e5a516b9310d6592e7368771c1ad3b6e6330f6bc0baa3db31643', 'txlist_hash': '5a2e9fbd9b52ee32b8e8bfff993ed92dc22510aa7448277a704176cf01e55b04'},
    950000: {'ledger_hash': '5a5e78b55ac294690229abff7ff8f74f390f3a47dc4d08a0bac40e2eM
89a5bed2', 'txlist_hash': 'f4fa9838fb38d3e5beffb760fae022dcc59c61c506dd28ac83ee48ba814d04b2'},
    1000000: {'ledger_hash': 'eafca6700b9fd8f3992f8a18316e9ad59480ef74a4e7737793c101878aba8e1a', 'txlist_hash': '03deb626e031f30acd394bf49c35e11a487cb11e55dff5ba9a3f6d04b460c7de'},
    1050000: {'ledger_hash': '8012ebaf4c6638173e88ecd3e7bb2242ab88a9bdf877fc32c42dbcd7d2d3bab1', 'txlist_hash': '896274fdba957961083b07b80634126bc9f0434b67d723ed1fa83157ce5cd9a7'},
    1100000: {'ledger_hash': '76357f917235daa180c904cdf5c44366eM
ef3e33539b7b0ba6a38f89582e82d22', 'txlist_hash': '36ecfd4b07f23176cd6960bc0adef97472c13793e53ac3df0eea0dd2e718a570'},
    1150000: {'ledger_hash': '5924f004bfdc3be449401c764808ebced542d2e06ba30c5984830292d1a926aa', 'txlist_hash': '9ff139dacf4b04293074e962153b972d25fa16d862dae05f7f3acc15e83c4fe8'},
    1200000: {'ledger_hash': 'a3d009bd2e0b838c185b8866233d7b4edaff87e5ec4cc4719578d1a8f9f8fe34', 'txlist_hash': '11dcf3a0ab714f05004a4e6c77fe425eb2a6427e4c98b7032412ab29363ffbb2'},
    1250000: {'ledger_hash': '37244453b4M
eac67d1dbfc0f60116cac90dab7b814d756653ad3d9a072fbac61a', 'txlist_hash': 'c01ed3113f8fd3a6b54f5cefafd842ebf7c314ce82922e36236414d820c5277a'},
    1300000: {'ledger_hash': 'a83c1cd582604130fd46f1304560caf0f4e3300f3ce7c3a89824b8901f13027f', 'txlist_hash': '67e663b75a80940941b8370ada4985be583edaa7ba454d49db9a864a7bb7979c'},
    1350000: {'ledger_hash': 'f96e6aff578896a4568fb69f72aa0a8b52eb9ebffefca4bd7368790341cd821d', 'txlist_hash': '83e7d31217af274b13889bd8b9f8f61afcd7996c2c8913e9b53b1d575f54b7c1'},
dger_hash': '85a23f6fee9ce9c80fa335729312183ff014920bbf297095ac77c4105fb67e17', 'txlist_hash': 'eee762f34a3f82e6332c58e0c256757d97ca308719323af78bf5924f08463e12'},
CONSENSUS_HASH_VERSION_REGTEST = 1
CHECKPOINTS_REGTEST = {
    config.BLOCK_FIRST_REGTEST: {'ledger_hash': '33cf0669a0d309d7e6b1bf79494613b69262b58c0ea03c9c221d955eb4c84fe5', 'txlist_hash': '33cf0669a0d309d7e6b1bf79494613b69262b58c0ea03c9c221d955eb4c84fe5'},
class ConsensusError(Exception):
def consensus_hash(db, field, previous_consensusM
    cursor = db.cursor()
    block_index = util.CURRENT_BLOCK_INDEX
    # Initialise previous hash on first block.
    if block_index <= config.BLOCK_FIRST:
        assert not previous_consensus_hash
        previous_consensus_hash = util.dhash_string(CONSENSUS_HASH_SEED)
    # Get previous hash.
    if not previous_consensus_hash:
            previous_consensus_hash = list(cursor.execute('''SELECT * FROM blocks WHERE block_index = ?''', (block_index - 1,)))[0][field]
            previous_consensus_hash = None
        if not previous_consensus_hash:
            raise ConsensusError('Empty previous {} for block {}. Please launch a `reparse`.'.format(field, block_index))
    # Calculate current hash.
    if config.TESTNET:
        consensus_hash_version = CONSENSUS_HASH_VERSION_TESTNET
    elif config.REGTEST:
        consensus_hash_version = CONSENSUS_HASH_VERSION_REGTEST
        consensus_hash_version = CONSENSUS_HASH_VERSION_MAINNET
    calculated_hash =M
 util.dhash_string(previous_consensus_hash + '{}{}'.format(consensus_hash_version, ''.join(content)))
    # Verify hash (if already in database) or save hash (if not).
    # NOTE: do not enforce this for messages_hashes, those are more informational (for now at least)
    found_hash = list(cursor.execute('''SELECT * FROM blocks WHERE block_index = ?''', (block_index,)))[0][field] or None
    if found_hash and field != 'messages_hash':
        # Check against existing value.
        if calculated_hash != found_hashM
            raise ConsensusError('Inconsistent {} for block {} (calculated {}, vs {} in database).'.format(
                field, block_index, calculated_hash, found_hash))
        # Save new hash.
        cursor.execute('''UPDATE blocks SET {} = ? WHERE block_index = ?'''.format(field), (calculated_hash, block_index))
    # Check against checkpoints.
    if config.TESTNET:
        checkpoints = CHECKPOINTS_TESTNET
    elif config.REGTEST:
        checkpoints = CHECKPOINTS_REGTEST
eckpoints = CHECKPOINTS_MAINNET
    if field != 'messages_hash' and block_index in checkpoints and checkpoints[block_index][field] != calculated_hash:
        raise ConsensusError('Incorrect {} hash for block {}.  Calculated {} but expected {}'.format(field, block_index, calculated_hash, checkpoints[block_index][field],))
    return calculated_hash, found_hash
class SanityError(Exception):
def asset_conservation(db):
    logger.debug('Checking for conservation of assets.')
    supplies = util.supplies(M
    held = util.held(db)
    for asset in supplies.keys():
        asset_issued = supplies[asset]
        asset_held = held[asset] if asset in held and held[asset] != None else 0
        if asset_issued != asset_held:
            raise SanityError('{} {} issued
 {} {} held'.format(util.value_out(db, asset_issued, asset), asset, util.value_out(db, asset_held, asset), asset))
        logger.debug('{} has been conserved ({} {} both issued and held)'.format(asset, util.value_out(db, asset_issued, asset), asset))M
class VersionError(Exception):
class VersionUpdateRequiredError(VersionError):
def check_change(protocol_change, change_name):
    # Check client version.
    if config.VERSION_MAJOR < protocol_change['minimum_version_major']:
        passed = False
    elif config.VERSION_MAJOR == protocol_change['minimum_version_major']:
        if config.VERSION_MINOR < protocol_change['minimum_version_minor']:
            passed = False
        elif config.VERSION_MINOR == protocol_changeM
['minimum_version_minor']:
            if config.VERSION_REVISION < protocol_change['minimum_version_revision']:
                passed = False
        explanation = 'Your version of {} is v{}, but, as of block {}, the minimum version is v{}.{}.{}. Reason:
. Please upgrade to the latest version and restart the server.'.format(
            config.APP_NAME, config.VERSION_STRING, protocol_change['block_index'], protocol_change['minimum_version_major'], protocol_change['minimum_version_minoM
            protocol_change['minimum_version_revision'], change_name)
        if util.CURRENT_BLOCK_INDEX >= protocol_change['block_index']:
            raise VersionUpdateRequiredError(explanation)
            warnings.warn(explanation)
def software_version():
    if config.FORCE:
    logger.debug('Checking version.')
        host = 'https://counterpartyxcp.github.io/counterparty-lib/counterpartylib/protocol_changes.json'
        response = requests.get(host, headers={'M
cache-control': 'no-cache'})
        versions = json.loads(response.text)
    except (requests.exceptions.ConnectionError, ConnectionRefusedError, ValueError) as e:
        logger.warning('Unable to check version! ' + str(sys.exc_info()[1]))
    for change_name in versions:
        protocol_change = versions[change_name]
            check_change(protocol_change, change_name)
        except VersionUpdateRequiredError as e:
            logger.error("Version Update Required", exc_info=sys.M
            sys.exit(config.EXITCODE_UPDATE_REQUIRED)
    logger.debug('Version check passed.')
class DatabaseVersionError(Exception):
    def __init__(self, message, reparse_block_index):
        super(DatabaseVersionError, self).__init__(message)
        self.reparse_block_index = reparse_block_index
def database_version(db):
    if config.FORCE:
    logger.debug('Checking database version.')
    version_major, version_minor = database.version(db)
    if version_major != config.VERM
        # Rollback database if major version has changed.
        raise DatabaseVersionError('Client major version number mismatch ({}
 {}).'.format(version_major, config.VERSION_MAJOR), config.BLOCK_FIRST)
    elif version_minor != config.VERSION_MINOR:
        # Reparse all transactions if minor version has changed.
        raise DatabaseVersionError('Client minor version number mismatch ({}
 {}).'.format(version_minor, config.VERSION_MINOR), None)
# vim: tabstop=8 expandtab shiftwidth=4 softtaM
"""Variables prefixed with `DEFAULT` should be able to be overridden by
configuration file and command
UNIT = 100000000        # The same across assets.
VERSION_REVISION = 1
VERSION_STRING = str(VERSION_MAJOR) + '.' + str(VERSION_MINOR) + '.' + str(VERSION_REVISION)
# Counterparty protocol
TXTYPE_FORMAT = '>I'
SHORT_TXTYPE_FORMAT = 'B'
TWO_WEEKS = 2 * 7 * 24 * 3600
MAX_EXPIRATION = 4 * 2016   # TM
MEMPOOL_BLOCK_HASH = 'mempool'
MEMPOOL_BLOCK_INDEX = 9999999
OP_RETURN_MAX_SIZE = 80  # bytes
# Currency agnosticism
BTC_NAME = 'Bitcoin'
XCP_NAME = 'Counterparty'
APP_NAME = XCP_NAME.lower()
DEFAULT_RPC_PORT_REGTEST = 24000
DEFAULT_RPC_PORT_TESTNET = 14000
DEFAULT_RPC_PORT = 4000
DEFAULT_BACKEND_PORT_REGTEST = 28332
DEFAULT_BACKEND_PORT_TESTNET = 18332
DEFAULT_BACKEND_PORT = 8332
DEFAULT_INDEXD_PORT_REGTEST = 28432
DEXD_PORT_TESTNET = 18432
DEFAULT_INDEXD_PORT = 8432
UNSPENDABLE_REGTEST = 'mvCounterpartyXXXXXXXXXXXXXXW24Hef'
UNSPENDABLE_TESTNET = 'mvCounterpartyXXXXXXXXXXXXXXW24Hef'
UNSPENDABLE_MAINNET = '1CounterpartyXXXXXXXXXXXXXXXUWLpVr'
ADDRESSVERSION_TESTNET = b'\x6f'
P2SH_ADDRESSVERSION_TESTNET = b'\xc4'
PRIVATEKEY_VERSION_TESTNET = b'\xef'
ADDRESSVERSION_MAINNET = b'\x00'
P2SH_ADDRESSVERSION_MAINNET = b'\x05'
PRIVATEKEY_VERSION_MAINNET = b'\x80'
ADDRESSVERSION_REGTEST = b'\x6f'
P2SH_ADDRESSVERSION_REGTEST = b'\xc4'
RIVATEKEY_VERSION_REGTEST = b'\xef'
MAGIC_BYTES_TESTNET = b'\xfa\xbf\xb5\xda'   # For bip-0010
MAGIC_BYTES_MAINNET = b'\xf9\xbe\xb4\xd9'   # For bip-0010
MAGIC_BYTES_REGTEST = b'\xda\xb5\xbf\xfa'
BLOCK_FIRST_TESTNET_TESTCOIN = 310000
BURN_START_TESTNET_TESTCOIN = 310000
BURN_END_TESTNET_TESTCOIN = 4017708     # Fifty years, at ten minutes per block.
BLOCK_FIRST_TESTNET = 310000
BLOCK_FIRST_TESTNET_HASH = '000000001f605ec6ee8d2c0d21bf3d3ded0a31ca837acc98893876213828989d'
BURN_START_TESTNET = 310000
T = 4017708              # Fifty years, at ten minutes per block.
BLOCK_FIRST_MAINNET_TESTCOIN = 278270
BURN_START_MAINNET_TESTCOIN = 278310
BURN_END_MAINNET_TESTCOIN = 2500000     # A long time.
BLOCK_FIRST_MAINNET = 278270
BLOCK_FIRST_MAINNET_HASH = '00000000000000017bac9a8e85660ad348050c789922d5f8fe544d473368be1a'
BURN_START_MAINNET = 278310
BURN_END_MAINNET = 283810
BLOCK_FIRST_REGTEST = 0
BLOCK_FIRST_REGTEST_HASH = '0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206'
BURN_START_REGTEST = 101
URN_END_REGTEST = 150000000
BLOCK_FIRST_REGTEST_TESTCOIN = 0
BURN_START_REGTEST_TESTCOIN = 101
BURN_END_REGTEST_TESTCOIN = 150
# NOTE: If the DUST_SIZE constants are changed, they MUST also be changed in counterblockd/lib/config.py as well
DEFAULT_REGULAR_DUST_SIZE = 546          # TODO: Revisit when dust size is adjusted in bitcoin core
DEFAULT_MULTISIG_DUST_SIZE = 7800        # <https://bitcointalk.org/index.php?topic=528023.msg7469941#msg7469941>
DEFAULT_OP_RETURN_VALUE = 0
KB_ESTIMATE_SMART = 1024
DEFAULT_FEE_PER_KB = 25000               # sane/low default, also used as minimum when estimated fee is used
ESTIMATE_FEE_PER_KB = True               # when True will use `estimatesmartfee` from bitcoind instead of DEFAULT_FEE_PER_KB
ESTIMATE_FEE_CONF_TARGET = 3
ESTIMATE_FEE_MODE = 'CONSERVATIVE'
DEFAULT_FEE_FRACTION_REQUIRED = .009   # 0.90%
DEFAULT_FEE_FRACTION_PROVIDED = .01    # 1.00%
DEFAULT_REQUESTS_TIMEOUT = 20   # 20 seconds
DEFAULT_RPC_BATCH_SIZE = 20     # A 1 MB M
block can hold about 4200 transactions.
EXITCODE_UPDATE_REQUIRED = 5
DEFAULT_CHECK_ASSET_CONSERVATION = True
BACKEND_RAW_TRANSACTIONS_CACHE_SIZE = 20000
BACKEND_RPC_BATCH_NUM_WORKERS = 6
UNDOLOG_MAX_PAST_BLOCKS = 100 #the number of past blocks that we store undolog history
DEFAULT_UTXO_LOCKS_MAX_ADDRESSES = 1000
DEFAULT_UTXO_LOCKS_MAX_AGE = 3.0 #in seconds
ADDRESS_OPTION_REQUIRE_MEMO = 1
ADDRESS_OPTION_MAX_VALUE = ADDRESS_OPTION_REQUIRE_MEMO # Or list of all the address options
API_LIMIT_ROWS = 1000
MEMPOOL_TXCOUNT_UPDATE_LIMIT=60000
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
logger = logging.getLogger(__name__)
from counterpartylib.lib import config
from counterpartylib.lib import util
from counterpartylib.lib import exceptions
from counterpartylib.lib import log
def rowtracer(cursor, sql):
    """Converts fetched SQL data intM
    for index, (name, type_) in enumerate(cursor.getdescription()):
        dictionary[name] = sql[index]
    return dictionary
def exectracer(cursor, sql, bindings):
    # This means that all changes to database must use a very simple syntax.
    # TODO: Need sanity checks here.
    sql = sql.lower()
    if sql.startswith('create trigger') or sql.startswith('drop trigger'):
        #CREATE TRIGGER stmts may include an "insert" or "update" as part of them
    array = sql.split('(')[0].split(' ')
    command = array[0]
    if 'insert' in sql:
        category = array[2]
    elif 'update' in sql:
        category = array[1]
        #CREATE TABLE, etc
    db = cursor.getconnection()
    dictionary = {'command': command, 'category': category, 'bindings': bindings}
        'blocks', 'transactions',
        'balances', 'messages', 'mempool', 'assets',
        'new_sends', 'new_issuances' # interim table fM
    skip_tables_block_messages = copy.copy(skip_tables)
    if command == 'update':
        # List message manually.
        skip_tables += ['orders', 'bets', 'rps', 'order_matches', 'bet_matches', 'rps_matches']
    # Record alteration in database.
    if category not in skip_tables:
        log.message(db, bindings['block_index'], command, category, bindings)
    # Record alteration in computation of message feed hash for the block
    if category not in skip_tables_block_messages:
    # don't include asset_longname as part of the messages hash
        #   until subassets are enabled
        if category == 'issuances' and not util.enabled('subassets'):
            if isinstance(bindings, dict) and 'asset_longname' in bindings: del bindings['asset_longname']
        # don't include memo as part of the messages hash
        #   until enhanced_sends are enabled
        if category == 'sends' and not util.enabled('enhanced_sends'):
            if isinstance(bindings, dict) and 'memo' in bindingsM
: del bindings['memo']
        sorted_bindings = sorted(bindings.items()) if isinstance(bindings, dict) else [bindings,]
        BLOCK_MESSAGES.append('{}{}{}'.format(command, category, sorted_bindings))
class DatabaseIntegrityError(exceptions.DatabaseError):
def get_connection(read_only=True, foreign_keys=True, integrity_check=True):
    """Connects to the SQLite database, returning a db `Connection` object"""
    logger.debug('Creating connection to `{}`.'.format(config.DATABASE))
        db = apsw.Connection(config.DATABASE, flags=apsw.SQLITE_OPEN_READONLY)
        db = apsw.Connection(config.DATABASE)
    cursor = db.cursor()
    # For integrity, security.
    if foreign_keys and not read_only:
        logger.info('Checking database foreign keys...')
        cursor.execute('''PRAGMA foreign_keys = ON''')
        cursor.execute('''PRAGMA defer_foreign_keys = ON''')
        rows = list(cursor.execute('''PRAGMA foreign_key_check'''))
                logger.debug('Foreign Key Error: {}'.format(row))
            raise exceptions.DatabaseError('Foreign key check failed.')
        # So that writers don
        cursor.execute('''PRAGMA journal_mode = WAL''')
        logger.info('Foreign key check completed.')
    # Make case sensitive the `LIKE` operator.
    # For insensitive queries use 'UPPER(fieldname) LIKE value.upper()''
    cursor.execute('''PRAGMA case_sensitive_like = ON''')
    if integrity_check:
 logger.info('Checking database integrity...')
        integral = False
        for i in range(10): # DUPE
                cursor.execute('''PRAGMA integrity_check''')
                rows = cursor.fetchall()
                if not (len(rows) == 1 and rows[0][0] == 'ok'):
                    raise exceptions.DatabaseError('Integrity check failed.')
                integral = True
                break
            except DatabaseIntegrityError:
                time.sleep(1)
                continue
        if not integral:
            raise exceptions.DatabaseError('Could not perform integrity check.')
        logger.info('Integrity check completed.')
    db.setrowtrace(rowtracer)
    db.setexectrace(exectracer)
    cursor = db.cursor()
    user_version = cursor.execute('PRAGMA user_version').fetchall()[0]['user_version']
    # manage old user_version
    if user_version == config.VERSION_MINOR:
        version_minor = user_version
        version_major = cM
        user_version = (config.VERSION_MAJOR * 1000) + version_minor
        cursor.execute('PRAGMA user_version = {}'.format(user_version))
        version_minor = user_version % 1000
        version_major = user_version // 1000
    return version_major, version_minor
def update_version(db):
    cursor = db.cursor()
    user_version = (config.VERSION_MAJOR * 1000) + config.VERSION_MINOR
    cursor.execute('PRAGMA user_version = {}'.format(user_version)) # Syntax?!
    logger.info('DaM
tabase version number updated.')
    logger.info('Starting database VACUUM. This may take awhile...')
    cursor = db.cursor()
    cursor.execute('VACUUM')
    logger.info('Database VACUUM completed.')
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
class DatabaseError(Exception):
class TransactionError(Exception):
class ParseTransactionError(Exception):
class AssetError (Exception):
class AssetIDError(AssetError):
class MessageError(Exception):
class ComposeError(MessageError):
class UnpackError(MessageError):
class ValidateError(MessageError):
class DecodeError(MessageError):
class PushDataDecodeError(DecodeError):
class BTCOnlyError(MessageError):
    def __init__(self, msg, decodedTx=None):
        super(BTCOnlyError, self).__init__(msg)
        self.decodedTx = decodedTx
class BalanceError(ExceM
class EncodingError(Exception):
class OptionsError(Exception):
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
logger = logging.getLogger(__name__)
from datetime import datetime
from dateutil.tz import tzlocal
from colorlog import ColoredFormatter
from counterpartylib.lib import config
from counterpartylib.lib import exceptions
m counterpartylib.lib import util
class ModuleLoggingFilter(logging.Filter):
    module level logging filter (NodeJS-style), ie:
        filters="*,-counterpartylib.lib,counterpartylib.lib.api"
         - counterpartycli.server
         - counterpartylib.lib.api
        but will not log:
         - counterpartylib.lib
         - counterpartylib.lib.backend.indexd
    def __init__(self, filters):
        self.filters = str(filters).split(",")
        self.catchall = "*" in selfM
        if self.catchall:
            self.filters.remove("*")
    def filter(self, record):
        Determine if specified record should be logged or not
        result = None
        for filter in self.filters:
            if filter[:1] == "-":
                if result is None and ModuleLoggingFilter.ismatch(record, filter[1:]):
                    result = False
                if ModuleLoggingFilter.ismatch(record, filter):
                    result = True
        if result is None:
            return self.catchall
        return result
    def ismatch(cls, record, name):
        Determine if the specified record matches the name, in the same way as original logging.Filter does, ie:
            'counterpartylib.lib' will match 'counterpartylib.lib.check'
        nlen = len(name)
        if nlen == 0:
            return True
        elif name == record.name:
            return True
        elif record.name.find(name, 0, nlenM
            return False
        return record.name[nlen] == "."
def set_logger(logger):
    global ROOT_LOGGER
    if ROOT_LOGGER is None:
        ROOT_LOGGER = logger
LOGGING_SETUP = False
LOGGING_TOFILE_SETUP = False
def set_up(logger, verbose=False, logfile=None, console_logfilter=None):
    global LOGGING_SETUP
    global LOGGING_TOFILE_SETUP
    def set_up_file_logging():
        assert logfile
        max_log_size = 20 * 1024 * 1024 # 20 MB
        if os.name == 'nt':
    from counterpartylib.lib import util_windows
            fileh = util_windows.SanitizedRotatingFileHandler(logfile, maxBytes=max_log_size, backupCount=5)
            fileh = logging.handlers.RotatingFileHandler(logfile, maxBytes=max_log_size, backupCount=5)
        fileh.setLevel(logging.DEBUG)
        LOGFORMAT = '%(asctime)s [%(levelname)s] %(message)s'
        formatter = logging.Formatter(LOGFORMAT, '%Y-%m-%d-T%H:%M:%S%z')
        fileh.setFormatter(formatter)
        logger.addHandler(fileh)
    if LOGGING_SETUP:
        if logfile and not LOGGING_TOFILE_SETUP:
             set_up_file_logging()
             LOGGING_TOFILE_SETUP = True
        logger.getChild('log.set_up').debug('logging already setup')
    LOGGING_SETUP = True
    log_level = logging.DEBUG if verbose else logging.INFO
    logger.setLevel(log_level)
    # Console Logging
    console = logging.StreamHandler()
    console.setLevel(log_level)
    # only add [%(name)s] to LOGFORMAT if we're using console_logfilter
GFORMAT = '%(log_color)s[%(asctime)s][%(levelname)s]' + ('' if console_logfilter is None else '[%(name)s]') + ' %(message)s%(reset)s'
    LOGCOLORS = {'WARNING': 'yellow', 'ERROR': 'red', 'CRITICAL': 'red'}
    formatter = ColoredFormatter(LOGFORMAT, "%Y-%m-%d %H:%M:%S", log_colors=LOGCOLORS)
    console.setFormatter(formatter)
    logger.addHandler(console)
    if console_logfilter:
        console.addFilter(ModuleLoggingFilter(console_logfilter))
        set_up_file_logging()
        LOGGING_TOFILE_SETUP = True
    # Quieten noisy libraries.
    requests_log = logging.getLogger("requests")
    requests_log.setLevel(log_level)
    requests_log.propagate = False
    urllib3_log = logging.getLogger('urllib3')
    urllib3_log.setLevel(log_level)
    urllib3_log.propagate = False
    # Disable InsecureRequestWarning
    requests.packages.urllib3.disable_warnings()
    return int(time.time())
def isodt (epoch_time):
        return datetime.froM
mtimestamp(epoch_time, tzlocal()).isoformat()
        return '<datetime>'
def message(db, block_index, command, category, bindings, tx_hash=None):
    cursor = db.cursor()
    # Get last message index.
    messages = list(cursor.execute('''SELECT * FROM messages
                                      WHERE message_index = (SELECT MAX(message_index) from messages)'''))
        assert len(messages) == 1
        message_index = messages[0]['message_index'] + 1
    # Not to be misleading
    if block_index == config.MEMPOOL_BLOCK_INDEX:
            del bindings['status']
            del bindings['block_index']
            del bindings['tx_index']
        except KeyError:
    # Handle binary data.
    for item in sorted(bindings.items()):
        if type(item[1]) == bytes:
            items.append((item[0], binascii.hexlify(item[1]).decode('ascii')))
            items.append(item)
_string = json.dumps(collections.OrderedDict(items))
    cursor.execute('insert into messages values(:message_index, :block_index, :command, :category, :bindings, :timestamp)',
                   (message_index, block_index, command, category, bindings_string, curr_time()))
    # Log only real transactions.
    if block_index != config.MEMPOOL_BLOCK_INDEX:
        log(db, command, category, bindings)
def log (db, command, category, bindings):
    cursor = db.cursor()
    for element in bindM
            str(bindings[element])
        except KeyError:
            bindings[element] = '<Error>'
    def output (quantity, asset):
            if asset not in ('fraction', 'leverage'):
                return str(util.value_out(db, quantity, asset)) + ' ' + asset
                return str(util.value_out(db, quantity, asset))
        except exceptions.AssetError:
            return '<AssetError>'
        except decimal.DivisionByZero:
      return '<DivisionByZero>'
        except TypeError:
            return '<None>'
    if command == 'update':
        if category == 'order':
            logger.debug('Database: set status of order {} to {}.'.format(bindings['tx_hash'], bindings['status']))
        elif category == 'bet':
            logger.debug('Database: set status of bet {} to {}.'.format(bindings['tx_hash'], bindings['status']))
        elif category == 'order_matches':
            logger.debug('Database: set status of order_match {} to {M
}.'.format(bindings['order_match_id'], bindings['status']))
        elif category == 'bet_matches':
            logger.debug('Database: set status of bet_match {} to {}.'.format(bindings['bet_match_id'], bindings['status']))
        elif category == 'dispensers':
            escrow_quantity = ''
            divisible = get_asset_info(cursor, bindings['asset'])['divisible']
            if divisible:
                if "escrow_quantity" in bindings:
                    escrow_quantity = "{:.8f}".format(bM
indings["escrow_quantity"]/config.UNIT)
            if ("action" in bindings) and bindings["action"] == 'refill dispenser':
                logger.info("Dispenser: {} refilled a dispenser with {} {}".format(bindings["source"],escrow_quantity,bindings["asset"]))
            elif "prev_status" in bindings: #There was a dispense
                if bindings["prev_status"] == 0:
                    if bindings["status"] == 10:
                        logger.info("Dispenser: {} closed dispenser M
for {} (dispenser empty)".format(bindings["source"],bindings["asset"]))
            elif bindings["status"] == 10: #Address closed the dispenser
                logger.info("Dispenser: {} closed dispenser for {} (operator closed)".format(bindings["source"],bindings["asset"]))
        # TODO: elif category == 'balances':
            # logger.debug('Database: set balance of {} in {} to {}.'.format(bindings['address'], bindings['asset'], output(bindings['quantity'], bindings['asset']).split(' ')[0]))
        if category == 'credits':
            logger.debug('Credit: {} to {} #{}# <{}>'.format(output(bindings['quantity'], bindings['asset']), bindings['address'], bindings['action'], bindings['event']))
        elif category == 'debits':
            logger.debug('Debit: {} from {} #{}# <{}>'.format(output(bindings['quantity'], bindings['asset']), bindings['address'], bindings['action'], bindings['event']))
        elif category == 'sends':
            logger.info('Send: {} from {} to {} ({}) [{}]M
'.format(output(bindings['quantity'], bindings['asset']), bindings['source'], bindings['destination'], bindings['tx_hash'], bindings['status']))
        elif category == 'orders':
            logger.info('Order: {} ordered {} for {} in {} blocks, with a provided fee of {:.8f} {} and a required fee of {:.8f} {} ({}) [{}]'.format(bindings['source'], output(bindings['give_quantity'], bindings['give_asset']), output(bindings['get_quantity'], bindings['get_asset']), bindings['expiration'], bindings['fee_provided'] / coM
nfig.UNIT, config.BTC, bindings['fee_required'] / config.UNIT, config.BTC, bindings['tx_hash'], bindings['status']))
        elif category == 'order_matches':
            logger.info('Order Match: {} for {} ({}) [{}]'.format(output(bindings['forward_quantity'], bindings['forward_asset']), output(bindings['backward_quantity'], bindings['backward_asset']), bindings['id'], bindings['status']))
        elif category == 'btcpays':
            logger.info('{} Payment: {} paid {} to {} for order match {} ({}) [{}]'.formM
at(config.BTC, bindings['source'], output(bindings['btc_amount'], config.BTC), bindings['destination'], bindings['order_match_id'], bindings['tx_hash'], bindings['status']))
        elif category == 'issuances':
            if (get_asset_issuances_quantity(cursor, bindings["asset"]) == 0) or (bindings['quantity'] > 0): #This is the first issuance or the creation of more supply, so we have to log the creation of the token
                if bindings['divisible']:
                    divisibility = 'divisible'
               unit = config.UNIT
                else:
                    divisibility = 'indivisible'
                    unit = 1
                try:
                    quantity = util.value_out(cursor, bindings['quantity'], None, divisible=bindings['divisible'])
                except Exception as e:
                    quantity = '?'
                if 'asset_longname' in bindings and bindings['asset_longname'] is not None:
                    logger.info('Subasset Issuance: {} created {} of {}M
 subasset {} as numeric asset {} ({}) [{}]'.format(bindings['source'], quantity, divisibility, bindings['asset_longname'], bindings['asset'], bindings['tx_hash'], bindings['status']))
                else:
                    logger.info('Issuance: {} created {} of {} asset {} ({}) [{}]'.format(bindings['source'], quantity, divisibility, bindings['asset'], bindings['tx_hash'], bindings['status']))
            if bindings['locked']:
                lock_issuance = get_lock_issuance(cursor, bindings["assM
                if (lock_issuance == None) or (lock_issuance['tx_hash'] == bindings['tx_hash']):
                    logger.info('Issuance: {} locked asset {} ({}) [{}]'.format(bindings['source'], bindings['asset'], bindings['tx_hash'], bindings['status']))
            if bindings['transfer']:
                logger.info('Issuance: {} transfered asset {} to {} ({}) [{}]'.format(bindings['source'], bindings['asset'], bindings['issuer'], bindings['tx_hash'], bindings['status']))
        elif category == 'broadcasts':
            if bindings['locked']:
                logger.info('Broadcast: {} locked his feed ({}) [{}]'.format(bindings['source'], bindings['tx_hash'], bindings['status']))
                logger.info('Broadcast: ' + bindings['source'] + ' at ' + isodt(bindings['timestamp']) + ' with a fee of {}%'.format(output(D(bindings['fee_fraction_int'] / 1e8), 'fraction')) + ' (' + bindings['tx_hash'] + ')' + ' [{}]'.format(bindings['status']))
 category == 'bets':
            logger.info('Bet: {} against {}, by {}, on {}'.format(output(bindings['wager_quantity'], config.XCP), output(bindings['counterwager_quantity'], config.XCP), bindings['source'], bindings['feed_address']))
        elif category == 'bet_matches':
            placeholder = ''
            if bindings['target_value'] >= 0:    # Only non
negative values are valid.
                placeholder = ' that ' + str(output(bindings['target_value'], 'value'))
            if bindings['leverage']:M
                placeholder += ', leveraged {}x'.format(output(bindings['leverage'] / 5040, 'leverage'))
            logger.info('Bet Match: {} for {} against {} for {} on {} at {}{} ({}) [{}]'.format(util.BET_TYPE_NAME[bindings['tx0_bet_type']], output(bindings['forward_quantity'], config.XCP), util.BET_TYPE_NAME[bindings['tx1_bet_type']], output(bindings['backward_quantity'], config.XCP), bindings['feed_address'], isodt(bindings['deadline']), placeholder, bindings['id'], bindings['status']))
        elif categoM
            logger.info('Dividend: {} paid {} per unit of {} ({}) [{}]'.format(bindings['source'], output(bindings['quantity_per_unit'], bindings['dividend_asset']), bindings['asset'], bindings['tx_hash'], bindings['status']))
        elif category == 'burns':
            logger.info('Burn: {} burned {} for {} ({}) [{}]'.format(bindings['source'], output(bindings['burned'], config.BTC), output(bindings['earned'], config.XCP), bindings['tx_hash'], bindings['status']))
        elif category == 'cM
            logger.info('Cancel: {} ({}) [{}]'.format(bindings['offer_hash'], bindings['tx_hash'], bindings['status']))
        elif category == 'rps':
            log_message = 'RPS: {} opens game with {} possible moves and a wager of {}'.format(bindings['source'], bindings['possible_moves'], output(bindings['wager'], 'XCP'))
            logger.info(log_message)
        elif category == 'rps_matches':
            log_message = 'RPS Match: {} is playing a {}-moves game with {} with a wager of {} ({}) [{}M
]'.format(bindings['tx0_address'], bindings['possible_moves'], bindings['tx1_address'], output(bindings['wager'], 'XCP'), bindings['id'], bindings['status'])
            logger.info(log_message)
        elif category == 'rpsresolves':
            if bindings['status'] == 'valid':
                rps_matches = list(cursor.execute('''SELECT * FROM rps_matches WHERE id = ?''', (bindings['rps_match_id'],)))
                assert len(rps_matches) == 1
                rps_match = rps_matches[0]
                log_mesM
sage = 'RPS Resolved: {} is playing {} on a {}-moves game with {} with a wager of {} ({}) [{}]'.format(rps_match['tx0_address'], bindings['move'], rps_match['possible_moves'], rps_match['tx1_address'], output(rps_match['wager'], 'XCP'), rps_match['id'], rps_match['status'])
                log_message = 'RPS Resolved: {} [{}]'.format(bindings['tx_hash'], bindings['status'])
            logger.info(log_message)
        elif category == 'order_expirations':
            logger.info('Expired order: {M
}'.format(bindings['order_hash']))
        elif category == 'order_match_expirations':
            logger.info('Expired Order Match awaiting payment: {}'.format(bindings['order_match_id']))
        elif category == 'bet_expirations':
            logger.info('Expired bet: {}'.format(bindings['bet_hash']))
        elif category == 'bet_match_expirations':
            logger.info('Expired Bet Match: {}'.format(bindings['bet_match_id']))
        elif category == 'bet_match_resolutions':
   cfd_type_id = util.BET_TYPE_ID['BullCFD'] + util.BET_TYPE_ID['BearCFD']
            equal_type_id = util.BET_TYPE_ID['Equal'] + util.BET_TYPE_ID['NotEqual']
            if bindings['bet_match_type_id'] == cfd_type_id:
                if bindings['settled']:
                    logger.info('Bet Match Settled: {} credited to the bull, {} credited to the bear, and {} credited to the feed address ({})'.format(output(bindings['bull_credit'], config.XCP), output(bindings['bear_credit'], config.XCP), output(bindings['M
fee'], config.XCP), bindings['bet_match_id']))
                else:
                    logger.info('Bet Match Force
Liquidated: {} credited to the bull, {} credited to the bear, and {} credited to the feed address ({})'.format(output(bindings['bull_credit'], config.XCP), output(bindings['bear_credit'], config.XCP), output(bindings['fee'], config.XCP), bindings['bet_match_id']))
            elif bindings['bet_match_type_id'] == equal_type_id:
                logger.info('Bet Match Settled: {} won the pot of {};M
 {} credited to the feed address ({})'.format(bindings['winner'], output(bindings['escrow_less_fee'], config.XCP), output(bindings['fee'], config.XCP), bindings['bet_match_id']))
        elif category == 'rps_expirations':
            logger.info('Expired RPS: {}'.format(bindings['rps_hash']))
        elif category == 'rps_match_expirations':
            logger.info('Expired RPS Match: {}'.format(bindings['rps_match_id']))
        elif category == 'destructions':
            asset_info = get_asset_info(cursor, bM
            quantity = bindings['quantity']
            if asset_info['divisible']:
                quantity = "{:.8f}".format(quantity/config.UNIT)
            logger.info('Destruction: {} destroyed {} {} with tag
({}) [{}]'.format(bindings['source'], quantity, bindings['asset'], bindings['tag'], bindings['tx_hash'], bindings['status']))
        elif category == 'dispensers':
            each_price = bindings['satoshirate']
            currency = config.BTC
            dispenser_label =M
            escrow_quantity = bindings['escrow_quantity']
            give_quantity = bindings['give_quantity']
            if (bindings['oracle_address'] != None) and util.enabled('oracle_dispensers'):
                each_price = "{:.2f}".format(each_price/100.0)
                oracle_last_price, oracle_fee, currency, oracle_last_updated = util.get_oracle_last_price(db, bindings['oracle_address'], bindings['block_index'])
                dispenser_label = 'oracle dispenser using {}'.forM
mat(bindings['oracle_address'])
                each_price = "{:.8f}".format(each_price/config.UNIT)
            divisible = get_asset_info(cursor, bindings['asset'])['divisible']
            if divisible:
                escrow_quantity = "{:.8f}".format(escrow_quantity/config.UNIT)
                give_quantity = "{:.8f}".format(give_quantity/config.UNIT)
            if bindings['status'] == 0:
                logger.info('Dispenser: {} opened a {} for aM
sset {} with {} balance, giving {} {} for each {} {}'.format(bindings['source'], dispenser_label, bindings['asset'], escrow_quantity, give_quantity, bindings['asset'], each_price, currency))
            elif bindings['status'] == 1:
                logger.info('Dispenser: {} (empty address) opened a {} for asset {} with {} balance, giving {} {} for each {} {}'.format(bindings['source'], dispenser_label, bindings['asset'], escrow_quantity, give_quantity, bindings['asset'], each_price, currency))
            elif binM
dings['status'] == 10:
                logger.info('Dispenser: {} closed a {} for asset {}'.format(bindings['source'], dispenser_label, bindings['asset']))
        elif category == 'dispenses':
            cursor.execute('SELECT * FROM dispensers WHERE tx_hash=:tx_hash', {
                'tx_hash': bindings['dispenser_tx_hash']
            dispensers = cursor.fetchall()
            dispenser = dispensers[0]
            if (dispenser["oracle_address"] != None) and util.enabled('oracle_dispeM
                tx_btc_amount = get_tx_info(cursor, bindings['tx_hash'])/config.UNIT
                oracle_last_price, oracle_fee, oracle_fiat_label, oracle_last_price_updated = util.get_oracle_last_price(db, dispenser["oracle_address"], bindings['block_index'])
                fiatpaid = round(tx_btc_amount*oracle_last_price,2)
                logger.info('Dispense: {} from {} to {} for {:.8f} {} ({} {}) ({})'.format(output(bindings['dispense_quantity'], bindings['asset']), bindings['souM
rce'], bindings['destination'], tx_btc_amount, config.BTC, fiatpaid, oracle_fiat_label, bindings['tx_hash']))
                logger.info('Dispense: {} from {} to {} ({})'.format(output(bindings['dispense_quantity'], bindings['asset']), bindings['source'], bindings['destination'], bindings['tx_hash']))
def get_lock_issuance(cursor, asset):
    cursor.execute('''SELECT * FROM issuances \
        WHERE (status = ? AND asset = ? AND locked = ?)
        ORDER BY tx_index ASC''', (M
'valid', asset, True))
    issuances = cursor.fetchall()
    if len(issuances) > 0:
        return issuances[0]
def get_asset_issuances_quantity(cursor, asset):
    cursor.execute('''SELECT COUNT(*) AS issuances_count FROM issuances \
        WHERE (status = ? AND asset = ?)
        ORDER BY tx_index DESC''', ('valid', asset))
    issuances = cursor.fetchall()
    return issuances[0]['issuances_count']
def get_asset_info(cursor, asset):
    if asset == config.BTC or asset == config.XCPM
        return {'divisible':True}
    cursor.execute('''SELECT * FROM issuances \
        WHERE (status = ? AND asset = ?)
        ORDER BY tx_index DESC''', ('valid', asset))
    issuances = cursor.fetchall()
    return issuances[0]
def get_tx_info(cursor, tx_hash):
    cursor.execute('SELECT * FROM transactions WHERE tx_hash=:tx_hash', {
        'tx_hash': tx_hash
    transactions = cursor.fetchall()
    transaction = transactions[0]
    return transaction["btc_amount"]
# vim: tabstop=8 expanM
dtab shiftwidth=4 softtabstop=4
#### message_type.py
logger = logging.getLogger(__name__)
from counterpartylib.lib import config
from counterpartylib.lib import util
def pack(message_type_id, block_index=None):
    # pack message ID into 1 byte if not zero
    if util.enabled('short_tx_type_id', block_index) and message_type_id > 0 and message_type_id < 256:
        return struct.pack(config.SHORT_TXTYPE_FORMAT, message_type_id)
    # pack into 4 bytes
truct.pack(config.TXTYPE_FORMAT, message_type_id)
# retuns both the message type id and the remainder of the message data
def unpack(packed_data, block_index=None):
    message_type_id = None
    message_remainder = None
    if len(packed_data) > 1:
        # try to read 1 byte first
        if util.enabled('short_tx_type_id', block_index):
            message_type_id = struct.unpack(config.SHORT_TXTYPE_FORMAT, packed_data[:1])[0]
            if message_type_id > 0:
                message_remainder = packed_dataM
                return (message_type_id, message_remainder)
    # First message byte was 0.  We will read 4 bytes
    if len(packed_data) > 4:
        message_type_id = struct.unpack(config.TXTYPE_FORMAT, packed_data[:4])[0]
        message_remainder = packed_data[4:]
    return (message_type_id, message_remainder)
None of the functions/objects in this module need be passed `db`.
Naming convention: a `pub` is either a pubkey or a pubkeyhash
from bitcoin.core.key import CPubKey
from bitcoin.bech32 import CBech32Data
from counterpartylib.lib import util
from counterpartylib.lib import config
from counterpartylib.lib import exceptions
b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
class InputError (Exception):
class AddressError(Exception):
class MultiSigAddressError(AddressError):
class VersionByteError (AddressError):
class Base58Error (AddressError):
class Base58ChecksumError (Base58Error):
def validate(address, allow_p2sh=True):
    """Make sure the address is valid.
    May throw `AddressError`.
    # Get array of pubkeyhashes to check.
    if is_multisig(address):
        pubkeyhashes = pubkeyhash_array(address)
        pubkeyhashes = [address]
    # Check validity by attempting to decode.
    for pubkeyhash in pubkeyhashes:
            if util.enabled('segwit_support'):
                if not is_bech32(pubkeyhasM
                    base58_check_decode(pubkeyhash, config.ADDRESSVERSION)
                base58_check_decode(pubkeyhash, config.ADDRESSVERSION)
        except VersionByteError as e:
            if not allow_p2sh:
                raise e
            base58_check_decode(pubkeyhash, config.P2SH_ADDRESSVERSION)
        except Base58Error as e:
            if not util.enabled('segwit_support') or not is_bech32(pubkeyhash):
                raise e
def base58_encode(binary):
    """Encode the adM
endian bytes to integer
    n = int('0x0' + util.hexlify(binary), 16)
    # Divide that integer into base58
        n, r = divmod(n, 58)
        res.append(b58_digits[r])
    res = ''.join(res[::-1])
def base58_check_encode(original, version):
    """Check if base58 encoding is valid."""
    b = binascii.unhexlify(bytes(original, 'utf-8'))
    binary = d + util.dhash(d)[:4]
    res = base58_encode(binary)
    # Encode leading zeros as base58 zeros
        if c == czero:
            pad += 1
    address = b58_digits[0] * pad + res
    if original != util.hexlify(base58_check_decode(address, version)):
        raise AddressError('encoded address does not decode properly')
def base58_decode(s):
    # Convert the string to an integer
        if c not in b58_digits:
 Base58Error('Not a valid Base58 character:
        digit = b58_digits.index(c)
    # Convert the integer to bytes
    res = binascii.unhexlify(h.encode('utf8'))
    # Add padding back.
    for c in s[:-1]:
        if c == b58_digits[0]:
            pad += 1
    k = b'\x00' * pad + res
def base58_check_decode_parts(s):
    """Decode from base58 and return partM
    k = base58_decode(s)
    addrbyte, data, chk0 = k[0:1], k[1:-4], k[-4:]
    return addrbyte, data, chk0
def base58_check_decode(s, version):
    """Decode from base58 and return data part."""
    addrbyte, data, chk0 = base58_check_decode_parts(s)
    if addrbyte != version:
        raise VersionByteError('incorrect version byte')
    chk1 = util.dhash(addrbyte + data)[:4]
    if chk0 != chk1:
        raise Base58ChecksumError('Checksum mismatch: 0x{}
 0x{}'.format(util.hexlify(chk0), util.hexlM
def is_multisig(address):
    """Check if the address is multi
    array = address.split('_')
    return len(array) > 1
def is_p2sh(address):
    if is_multisig(address):
        return False
        base58_check_decode(address, config.P2SH_ADDRESSVERSION)
    except (VersionByteError, Base58Error):
        return False
def is_bech32(address):
        b32data = CBech32Data(address)
def is_fully_valid(pubkey_bin):
    """Check if the public key is valid."""
    cpubkey = CPubKey(pubkey_bin)
    return cpubkey.is_fullyvalid
def make_canonical(address):
    """Return canonical version of the address."""
    if is_multisig(address):
        signatures_required, pubkeyhashes, signatures_possible = extract_array(address)
            [base58_check_decode(pubkeyhash, config.ADDRESSVERSION) for pubkeyhash in pubkeyhashes]
        except Base58Error:
            raise MultiSigAddreM
signature address must use PubKeyHashes, not public keys.')
        return construct_array(signatures_required, pubkeyhashes, signatures_possible)
        return address
def test_array(signatures_required, pubs, signatures_possible):
    """Check if multi
signature data is valid."""
        signatures_required, signatures_possible = int(signatures_required), int(signatures_possible)
    except (ValueError, TypeError):
        raise MultiSigAddressError('Signature values not inM
    if signatures_required < 1 or signatures_required > 3:
        raise MultiSigAddressError('Invalid signatures_required.')
    if signatures_possible < 2 or signatures_possible > 3:
        raise MultiSigAddressError('Invalid signatures_possible.')
    for pubkey in pubs:
        if '_' in pubkey:
            raise MultiSigAddressError('Invalid characters in pubkeys/pubkeyhashes.')
    if signatures_possible != len(pubs):
        raise InputError('Incorrect number of pubkeys/pubkeyhashes in multi
def construct_array(signatures_required, pubs, signatures_possible):
    """Create a multi
signature address."""
    test_array(signatures_required, pubs, signatures_possible)
    address = '_'.join([str(signatures_required)] + sorted(pubs) + [str(signatures_possible)])
def extract_array(address):
    """Extract data from multi
signature address."""
    assert is_multisig(address)
    array = address.split('_')
    signatures_required, pubs, signatures_possible = array[0],M
 sorted(array[1:-1]), array[-1]
    test_array(signatures_required, pubs, signatures_possible)
    return int(signatures_required), pubs, int(signatures_possible)
def pubkeyhash_array(address):
    """Return PubKeyHashes from an address."""
    signatures_required, pubs, signatures_possible = extract_array(address)
    if not all([is_pubkeyhash(pub) for pub in pubs]):
        raise MultiSigAddressError('Invalid PubKeyHashes. Multi
signature address must use PubKeyHashes, not public keys.')
    pubkeyhashes = pubM
    return pubkeyhashes
    x = hashlib.sha256(x).digest()
    m = hashlib.new('ripemd160')
    return m.digest()
def pubkey_to_pubkeyhash(pubkey):
    """Convert public key to PubKeyHash."""
    pubkeyhash = hash160(pubkey)
    pubkey = base58_check_encode(binascii.hexlify(pubkeyhash).decode('utf-8'), config.ADDRESSVERSION)
def pubkey_to_p2whash(pubkey):
    """Convert public key to PayToWitness."""
    pubkeyhash = hash160(pubkey)
    pubkey = CBech32Data.froM
m_bytes(0, pubkeyhash)
    return str(pubkey)
def bech32_to_scripthash(address):
    bech32 = CBech32Data(address)
    return bytes(bech32)
def get_asm(scriptpubkey):
    # TODO: When is an exception thrown here? Can this `try` block be tighter? Can it be replaced by a conditional?
        # TODO: This should be `for element in scriptpubkey`.
        for op in scriptpubkey:
            if type(op) == bitcoinlib.core.script.CScriptOp:
                # TODO: `op = element`
 asm.append(str(op))
                # TODO: `data = element` (?)
                asm.append(op)
    except bitcoinlib.core.script.CScriptTruncatedPushDataError:
        raise exceptions.PushDataDecodeError('invalid pushdata due to truncation')
        raise exceptions.DecodeError('empty output')
def get_checksig(asm):
    if len(asm) == 5 and asm[0] == 'OP_DUP' and asm[1] == 'OP_HASH160' and asm[3] == 'OP_EQUALVERIFY' and asm[4] == 'OP_CHECKSIG':
        if type(pubkeyhash) == bytes:
            return pubkeyhash
    raise exceptions.DecodeError('invalid OP_CHECKSIG')
def get_checkmultisig(asm):
    if len(asm) == 5 and asm[3] == 2 and asm[4] == 'OP_CHECKMULTISIG':
        pubkeys, signatures_required = asm[1:3], asm[0]
        if all([type(pubkey) == bytes for pubkey in pubkeys]):
            return pubkeys, signatures_required
    if len(asm) == 6 and asm[4] == 3 and asm[5] == 'OP_CHECKMULTISIG':
s, signatures_required = asm[1:4], asm[0]
        if all([type(pubkey) == bytes for pubkey in pubkeys]):
            return pubkeys, signatures_required
    raise exceptions.DecodeError('invalid OP_CHECKMULTISIG')
def scriptpubkey_to_address(scriptpubkey):
    asm = get_asm(scriptpubkey)
    if asm[-1] == 'OP_CHECKSIG':
            checksig = get_checksig(asm)
        except exceptions.DecodeError:  # coinbase
            return None
        return base58_check_encode(binascii.hexlify(checksig).decoM
de('utf-8'), config.ADDRESSVERSION)
    elif asm[-1] == 'OP_CHECKMULTISIG':
        pubkeys, signatures_required = get_checkmultisig(asm)
        pubkeyhashes = [pubkey_to_pubkeyhash(pubkey) for pubkey in pubkeys]
        return construct_array(signatures_required, pubkeyhashes, len(pubkeyhashes))
    elif len(asm) == 3 and asm[0] == 'OP_HASH160' and asm[2] == 'OP_EQUAL':
        return base58_check_encode(binascii.hexlify(asm[1]).decode('utf-8'), config.P2SH_ADDRESSVERSION)
# TODO: Use `pythonM
-bitcointools` instead. (Get rid of `pycoin` dependency.)
from pycoin.encoding import wif_to_tuple_of_secret_exponent_compressed, public_pair_to_sec, EncodingError
from pycoin.ecdsa import generator_secp256k1, public_pair_for_secret_exponent
class AltcoinSupportError (Exception): pass
def private_key_to_public_key(private_key_wif):
    """Convert private key to public key."""
    if config.TESTNET:
        allowable_wif_prefixes = [config.PRIVATEKEY_VERSION_TESTNET]
    elif config.REGTEST:
        allowable_wif_pM
refixes = [config.PRIVATEKEY_VERSION_REGTEST]
        allowable_wif_prefixes = [config.PRIVATEKEY_VERSION_MAINNET]
        secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed(
                private_key_wif, allowable_wif_prefixes=allowable_wif_prefixes)
    except EncodingError:
        raise AltcoinSupportError('pycoin: unsupported WIF prefix')
    public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent)
    public_key = public_pair_to_sec(publicM
_pair, compressed=compressed)
    public_key_hex = binascii.hexlify(public_key).decode('utf-8')
    return public_key_hex
def is_pubkeyhash(monosig_address):
    """Check if PubKeyHash is valid P2PKH address. """
    assert not is_multisig(monosig_address)
        base58_check_decode(monosig_address, config.ADDRESSVERSION)
    except (Base58Error, VersionByteError):
        return False
def make_pubkeyhash(address):
    """Create a new PubKeyHash."""
    if is_multisig(address):
   signatures_required, pubs, signatures_possible = extract_array(address)
        pubkeyhashes = []
        for pub in pubs:
            if is_pubkeyhash(pub):
                pubkeyhash = pub
                pubkeyhash = pubkey_to_pubkeyhash(binascii.unhexlify(bytes(pub, 'utf-8')))
            pubkeyhashes.append(pubkeyhash)
        pubkeyhash_address = construct_array(signatures_required, pubkeyhashes, signatures_possible)
        if util.enabled('segwit_support') and is_bech32(addresM
            pubkeyhash_address = address # Some bech32 addresses are valid base58 data
        elif is_pubkeyhash(address):
            pubkeyhash_address = address
        elif is_p2sh(address):
            pubkeyhash_address = address
            pubkeyhash_address = pubkey_to_pubkeyhash(binascii.unhexlify(bytes(address, 'utf-8')))
    return pubkeyhash_address
def extract_pubkeys(pub):
    """Assume pubkey if not pubkeyhash. (Check validity later.)"""
    if is_multisig(pub):
        _, pubs, _ = extract_array(pub)
        for pub in pubs:
            if not is_pubkeyhash(pub):
                pubkeys.append(pub)
    elif is_p2sh(pub):
    elif util.enabled('segwit_support') and is_bech32(pub):
        if not is_pubkeyhash(pub):
            pubkeys.append(pub)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
#### transaction.py
Construct and serialize the Bitcoin transactions that are Counterparty transactions.
This module contains no consensus
logger = logging.getLogger(__name__)
import bitcoin as bitcoinlib
from bitcoin.core.script import CScript
from bitcoin.core import x, CTransaction
from bitcoin.core import b2lx
from counterpartylib.lib import blocks
from counterpartylib.lib import config
from counterpartylib.lib import excM
from counterpartylib.lib import util
from counterpartylib.lib import script
from counterpartylib.lib import backend
from counterpartylib.lib import arc4
from counterpartylib.lib.transaction_helper import serializer, p2sh_encoding
OP_PUSHDATA1 = b'\x4c'
OP_HASH160 = b'\xa9'
OP_EQUALVERIFY = b'\x88'
OP_CHECKSIG = b'\xac'
OP_CHECKMULTISIG = b'\xae'
 is None or DictCache per address
# set higher than the max number of UTXOs we should expect to
# manage in an aging cache for any one source address, at any one period
UTXO_LOCKS_PER_ADDRESS_MAXSIZE = 5000
# UTXO_P2SH_ENCODING_LOCKS is TTLCache for UTXOs that are used for chaining p2sh encoding
#  instead of a simple (txid, vout) key we use [(vin.prevout.hash, vin.prevout.n) for vin tx1.vin]
UTXO_P2SH_ENCODING_LOCKS = None
# we cache the make_outkey_vin to avoid having to fetch raw txs too oftenM
UTXO_P2SH_ENCODING_LOCKS_CACHE = None
    global UTXO_LOCKS, UTXO_P2SH_ENCODING_LOCKS, UTXO_P2SH_ENCODING_LOCKS_CACHE
    if config.UTXO_LOCKS_MAX_ADDRESSES > 0:  # initialize if configured
        UTXO_LOCKS = util.DictCache(size=config.UTXO_LOCKS_MAX_ADDRESSES)
    UTXO_P2SH_ENCODING_LOCKS = cachetools.TTLCache(10000, 180)
    UTXO_P2SH_ENCODING_LOCKS_CACHE = cachetools.TTLCache(1000, 600)
def print_coin(coin):
    return 'amount: {:.8f}; txid: {}; vout: {}; confirmations: {}'.format(coinM
['amount'], coin['txid'], coin['vout'], coin.get('confirmations', '?')) # simplify and make deterministic
    """ Yield successive n
sized chunks from l.
    for i in range(0, len(l), n):
        yield l[i:i+n]
def make_outkey(output):
    return '{}{}'.format(output['txid'], output['vout'])
def make_outkey_vin_txid(txid, vout):
    global UTXO_P2SH_ENCODING_LOCKS_CACHE
    if (txid, vout) not in UTXO_P2SH_ENCODING_LOCKS_CACHE:
        txhex = backend.getrawtransaction(txid, verboM
        UTXO_P2SH_ENCODING_LOCKS_CACHE[(txid, vout)] = make_outkey_vin(txhex, vout)
    return UTXO_P2SH_ENCODING_LOCKS_CACHE[(txid, vout)]
def make_outkey_vin(txhex, vout):
    txbin = binascii.unhexlify(txhex) if isinstance(txhex, str) else txhex
    assert isinstance(vout, int)
    tx = bitcoinlib.core.CTransaction.deserialize(txbin)
    outkey = [(vin.prevout.hash, vin.prevout.n) for vin in tx.vin]
    outkey = hashlib.sha256(("%s%s" % (outkey, vout)).encode('ascii')).digest()
def get_dust_return_pubkey(source, provided_pubkeys, encoding):
    """Return the pubkey to which dust from data outputs will be sent.
    This pubkey is used in multi-sig data outputs (as the only real pubkey) to
    make those the outputs spendable. It is derived from the source address, so
    that the dust is spendable by the creator of the transaction.
    # Get hex dust return pubkey.
    if script.is_multisig(source):
        a, self_pubkeys, b = script.extract_array(backend.multisig_pubkeyhashes_tM
o_pubkeys(source, provided_pubkeys))
        dust_return_pubkey_hex = self_pubkeys[0]
        dust_return_pubkey_hex = backend.pubkeyhash_to_pubkey(source, provided_pubkeys)
    # Convert hex public key into the (binary) dust return pubkey.
        dust_return_pubkey = binascii.unhexlify(dust_return_pubkey_hex)
    except binascii.Error:
        raise script.InputError('Invalid private key.')
    return dust_return_pubkey
def construct_coin_selection(encoding, data_array, source, allow_unconfM
irmed_inputs, unspent_tx_hash, custom_inputs,
                             fee_per_kb, estimate_fee_per_kb, estimate_fee_per_kb_nblocks, exact_fee, size_for_fee, fee_provided, destination_btc_out, data_btc_out,
                             regular_dust_size, disable_utxo_locks):
    global UTXO_LOCKS, UTXO_P2SH_ENCODING_LOCKS
    # Array of UTXOs, as retrieved by listunspent function from bitcoind
    if custom_inputs:
        use_inputs = unspent = custom_inputs
        if unspent_tx_hash is not None:
            unspent = backend.get_unspent_txouts(source, unconfirmed=allow_unconfirmed_inputs, unspent_tx_hash=unspent_tx_hash)
            unspent = backend.get_unspent_txouts(source, unconfirmed=allow_unconfirmed_inputs)
        filter_unspents_utxo_locks = []
        if UTXO_LOCKS is not None and source in UTXO_LOCKS:
            filter_unspents_utxo_locks = UTXO_LOCKS[source].keys()
        filter_unspents_p2sh_locks = UTXO_P2SH_ENCODING_LOCKS.keys()
        # filter out any locked UTXOs to prevM
ent creating transactions that spend the same UTXO when they're created at the same time
        filtered_unspent = []
        for output in unspent:
            if make_outkey(output) not in filter_unspents_utxo_locks and make_outkey_vin_txid(output['txid'], output['vout']) not in filter_unspents_p2sh_locks:
                filtered_unspent.append(output)
        unspent = filtered_unspent
        unspent = backend.sort_unspent_txouts(unspent)
        logger.debug('Sorted candidate UTXOs: {}'.format([print_coin(cM
oin) for coin in unspent]))
        use_inputs = unspent
    # use backend estimated fee_per_kb
    if estimate_fee_per_kb:
        estimated_fee_per_kb = backend.fee_per_kb(estimate_fee_per_kb_nblocks, config.ESTIMATE_FEE_MODE)
        if estimated_fee_per_kb is not None:
            fee_per_kb = max(estimated_fee_per_kb, fee_per_kb)  # never drop below the default fee_per_kb
    logger.debug('Fee/KB {:.8f}'.format(fee_per_kb / config.UNIT))
    change_quantity = 0
    final_fee = fee_per_kb
    desired_input_count = 1
    if encoding == 'multisig' and data_array and util.enabled('bytespersigop'):
        desired_input_count = len(data_array) * 2
    # pop inputs until we can pay for the fee
    for coin in use_inputs:
        logger.debug('New input: {}'.format(print_coin(coin)))
        inputs.append(coin)
        btc_in += round(coin['amount'] * config.UNIT)
        # If exact fee is specified, use that. Otherwise, calculate size of tx
        # and base M
fee on that (plus provide a minimum fee for selling BTC).
        size = 181 * len(inputs) + size_for_fee + 10
        if exact_fee:
            final_fee = exact_fee
            necessary_fee = int(size / 1000 * fee_per_kb)
            final_fee = max(fee_provided, necessary_fee)
            logger.getChild('p2shdebug').debug('final_fee inputs: %d size: %d final_fee %s' % (len(inputs), size, final_fee))
        # Check if good.
        btc_out = destination_btc_out + data_btc_out
        change_quanM
tity = btc_in - (btc_out + final_fee)
        logger.debug('Size: {} Fee: {:.8f} Change quantity: {:.8f} BTC'.format(size, final_fee / config.UNIT, change_quantity / config.UNIT))
        # If change is necessary, must not be a dust output.
        if change_quantity == 0 or change_quantity >= regular_dust_size:
            sufficient_funds = True
            if len(inputs) >= desired_input_count:
                break
    if not sufficient_funds:
        # Approximate needed change, fee by with most recently calcM
        # quantities.
        btc_out = destination_btc_out + data_btc_out
        total_btc_out = btc_out + max(change_quantity, 0) + final_fee
        raise exceptions.BalanceError('Insufficient {} at address {}. (Need approximately {} {}.) To spend unconfirmed coins, use the flag `--unconfirmed`. (Unconfirmed coins cannot be spent from multi
sig addresses.)'.format(config.BTC, source, total_btc_out / config.UNIT, config.BTC))
    # Lock the source's inputs (UTXOs) chosen for this transaction
O_LOCKS is not None and not disable_utxo_locks:
        if source not in UTXO_LOCKS:
            UTXO_LOCKS[source] = cachetools.TTLCache(
                UTXO_LOCKS_PER_ADDRESS_MAXSIZE, config.UTXO_LOCKS_MAX_AGE)
        for input in inputs:
            UTXO_LOCKS[source][make_outkey(input)] = input
        logger.debug("UTXO locks: Potentials ({}): {}, Used: {}, locked UTXOs: {}".format(
            len(unspent), [make_outkey(coin) for coin in unspent],
            [make_outkey(input) for input in inputs], listM
(UTXO_LOCKS[source].keys())))
    # ensure inputs have scriptPubKey
    #   this is not provided by indexd
    inputs = backend.ensure_script_pub_key_for_inputs(inputs)
    return inputs, change_quantity, btc_in, final_fee
def select_any_coin_from_source(source, allow_unconfirmed_inputs=True, disable_utxo_locks=False):
    ''' Get the first (biggest) input from the source address '''
    global UTXO_LOCKS
    # Array of UTXOs, as retrieved by listunspent function from bitcoind
    unspent = backend.get_unspent_M
txouts(source, unconfirmed=allow_unconfirmed_inputs)
    filter_unspents_utxo_locks = []
    if UTXO_LOCKS is not None and source in UTXO_LOCKS:
        filter_unspents_utxo_locks = UTXO_LOCKS[source].keys()
    # filter out any locked UTXOs to prevent creating transactions that spend the same UTXO when they're created at the same time
    filtered_unspent = []
    for output in unspent:
        if make_outkey(output) not in filter_unspents_utxo_locks:
            filtered_unspent.append(output)
    unspent = backend.sort_unspent_txouts(unspent)
    # use the first input
    input = unspent[0]
    if input is None:
    # Lock the source's inputs (UTXOs) chosen for this transaction
    if UTXO_LOCKS is not None and not disable_utxo_locks:
        if source not in UTXO_LOCKS:
            UTXO_LOCKS[source] = cachetools.TTLCache(
                UTXO_LOCKS_PER_ADDRESS_MAXSIZE, config.UTXO_LOCKS_MAX_AGE)
        UTXO_LOCKS[source][make_outkey(input)] = input
def return_result(tx_hexes, old_style_api):
        tx_hexes = list(filter(None, tx_hexes))  # filter out None
        if old_style_api:
            if len(tx_hexes) != 1:
                raise Exception("Can't do 2 TXs with old_style_api")
            return tx_hexes[0]
            if len(tx_hexes) == 1:
                return tx_hexes[0]
                return tx_hexes
def construct (db, tx_info, encoding='auto',
               fee_per_kb=config.DEFAULT_FEE_PER_M
               estimate_fee_per_kb=None, estimate_fee_per_kb_conf_target=config.ESTIMATE_FEE_CONF_TARGET,
               estimate_fee_per_kb_mode=config.ESTIMATE_FEE_MODE, estimate_fee_per_kb_nblocks=config.ESTIMATE_FEE_CONF_TARGET,
               regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE,
               multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE,
               op_return_value=config.DEFAULT_OP_RETURN_VALUE,
               exact_fee=None, fee_provided=0, provided_pubkeys=None, dust_return_puM
               allow_unconfirmed_inputs=False, unspent_tx_hash=None, custom_inputs=None, disable_utxo_locks=False, extended_tx_info=False,
               old_style_api=None, segwit=False,
               p2sh_source_multisig_pubkeys=None, p2sh_source_multisig_pubkeys_required=None, p2sh_pretx_txid=None,):
    if estimate_fee_per_kb is None:
        estimate_fee_per_kb = config.ESTIMATE_FEE_PER_KB
    global UTXO_LOCKS, UTXO_P2SH_ENCODING_LOCKS
    # lazy assign from config, because when set as default M
it's evaluated before it's configured
    if old_style_api is None:
        old_style_api = config.OLD_STYLE_API
    (source, destination_outputs, data) = tx_info
    if dust_return_pubkey:
        dust_return_pubkey = binascii.unhexlify(dust_return_pubkey)
    if p2sh_source_multisig_pubkeys:
        p2sh_source_multisig_pubkeys = [binascii.unhexlify(p) for p in p2sh_source_multisig_pubkeys]
        # If public key is necessary for construction of (unsigned)
        # transaction, use the public M
key provided, or find it from the
        # blockchain.
        script.validate(source)
    source_is_p2sh = script.is_p2sh(source)
    # Normalize source
    if script.is_multisig(source):
        source_address = backend.multisig_pubkeyhashes_to_pubkeys(source, provided_pubkeys)
        source_address = source
    # Sanity checks.
    if exact_fee and not isinstance(exact_fee, int):
        raise exceptions.TransactionError('Exact fees must be in satoshis.')
    if not isinstance(fee_pM
        raise exceptions.TransactionError('Fee provided must be in satoshis.')
    '''Determine encoding method'''
        desired_encoding = encoding
        # Data encoding methods (choose and validate).
        if desired_encoding == 'auto':
            if len(data) + len(config.PREFIX) <= config.OP_RETURN_MAX_SIZE:
                encoding = 'opreturn'
                encoding = 'p2sh' if not old_style_api and util.enabled('p2sh_encoding') else 'multisig'  # p2sh M
is not possible with old_style_api
        elif desired_encoding == 'p2sh' and not util.enabled('p2sh_encoding'):
            raise exceptions.TransactionError('P2SH encoding not enabled yet')
        elif encoding not in ('pubkeyhash', 'multisig', 'opreturn', 'p2sh'):
            raise exceptions.TransactionError('Unknown encoding
        encoding = None
    '''Destinations'''
    # Destination outputs.
        # Replace multi
sig addresses with multi
        # destination output isn
t a dust output. Set null values to dust size.
    destination_outputs_new = []
    if encoding != 'p2sh':
        for (address, value) in destination_outputs:
            # Value.
            if script.is_multisig(address):
                dust_size = multisig_dust_size
                dust_size = regular_dust_size
            if value == None:
                value = dust_size
            elif value < dust_size:
                raise exceptions.TrM
ansactionError('Destination output is dust.')
            # Address.
            script.validate(address)
            if script.is_multisig(address):
                destination_outputs_new.append((backend.multisig_pubkeyhashes_to_pubkeys(address, provided_pubkeys), value))
                destination_outputs_new.append((address, value))
    destination_outputs = destination_outputs_new
    destination_btc_out = sum([value for address, value in destination_outputs])
        # @TODO: p2sh encoding require signable dust key
        if encoding == 'multisig':
            # dust_return_pubkey should be set or explicitly set to False to use the default configured for the node
            #  the default for the node is optional so could fail
            if (source_is_p2sh and dust_return_pubkey is None) or (dust_return_pubkey is False and config.P2SH_DUST_RETURN_PUBKEY is None):
                raise exceptions.TransactionError("Can't use multisig encoding when source is P2SH and M
no dust_return_pubkey is provided.")
            elif dust_return_pubkey is False:
                dust_return_pubkey = binascii.unhexlify(config.P2SH_DUST_RETURN_PUBKEY)
        if not dust_return_pubkey:
            if encoding == 'multisig' or encoding == 'p2sh' and not source_is_p2sh:
                dust_return_pubkey = get_dust_return_pubkey(source, provided_pubkeys, encoding)
                dust_return_pubkey = None
        # Divide data into chunks.
        if encoding == 'pubkeyhash':
            # Prefix is also a suffix here.
            chunk_size = 20 - 1 - 8
        elif encoding == 'multisig':
            # Two pubkeys, minus length byte, minus prefix, minus two nonces,
            # minus two sign bytes.
            chunk_size = (33 * 2) - 1 - 8 - 2 - 2
        elif encoding == 'p2sh':
            pubkeylength = -1
            if dust_return_pubkey is not None:
                pubkeylength = len(dust_return_pubkey)
            chunk_size = p2sh_encoding.maximum_data_chunk_size(pubkeylengM
        elif encoding == 'opreturn':
            chunk_size = config.OP_RETURN_MAX_SIZE
            if len(data) + len(config.PREFIX) > chunk_size:
                raise exceptions.TransactionError('One `OP_RETURN` output per transaction.')
        data_array = list(chunks(data, chunk_size))
        # Data outputs.
        if encoding == 'multisig':
            data_value = multisig_dust_size
        elif encoding == 'p2sh':
            data_value = 0   # this will be calculated later
        elif encoding == M
            data_value = op_return_value
            data_value = regular_dust_size
        data_output = (data_array, data_value)
        data_value = 0
        data_array = []
        data_output = None
        dust_return_pubkey = None
    data_btc_out = data_value * len(data_array)
    logger.getChild('p2shdebug').debug('data_btc_out=%s (data_value=%d len(data_array)=%d)' % (data_btc_out, data_value, len(data_array)))
    # Calculate collective size of outputs, for fee calculation.
    p2pkhsize = 25 + 9
    if encoding == 'multisig':
        data_output_size = 81       # 71 for the data
    elif encoding == 'opreturn':
        # prefix + data + 10 bytes script overhead
        data_output_size = len(config.PREFIX) + 10
        if data is not None:
            data_output_size = data_output_size + len(data)
        data_output_size = p2pkhsize   # Pay
PubKeyHash (25 for the dM
    outputs_size = (p2pkhsize * len(destination_outputs)) + (len(data_array) * data_output_size)
    if encoding == 'p2sh':
        # calculate all the p2sh outputs
        size_for_fee, datatx_necessary_fee, data_value, data_btc_out = p2sh_encoding.calculate_outputs(destination_outputs, data_array, fee_per_kb, exact_fee)
        # replace the data value
        data_output = (data_array, data_value)
        sum_data_output_size = len(data_array) * data_output_size
        size_for_fee = ((25 + 9) M
* len(destination_outputs)) + sum_data_output_size
    if not (encoding == 'p2sh' and p2sh_pretx_txid):
        inputs, change_quantity, n_btc_in, n_final_fee = construct_coin_selection(
            encoding, data_array,
            source, allow_unconfirmed_inputs, unspent_tx_hash, custom_inputs,
            fee_per_kb, estimate_fee_per_kb, estimate_fee_per_kb_nblocks,
            exact_fee, size_for_fee, fee_provided, destination_btc_out, data_btc_out,
            regular_dust_size, disable_utxo_locks
        btc_in = n_btc_in
        final_fee = n_final_fee
        # when encoding is P2SH and the pretx txid is passed we can skip coinselection
        inputs, change_quantity = None, None
    if change_quantity:
        change_output = (source_address, change_quantity)
        change_output = None
    unsigned_pretx_hex = None
    unsigned_tx_hex = None
    pretx_txid = None
    if encoding == 'p2sh':
        assert not (segwit and p2sh_pretx_txid)  # shouldn't do old stylM
e with segwit enabled
        if p2sh_pretx_txid:
            pretx_txid = p2sh_pretx_txid if isinstance(p2sh_pretx_txid, bytes) else binascii.unhexlify(p2sh_pretx_txid)
            unsigned_pretx = None
            destination_value_sum = sum([value for (destination, value) in destination_outputs])
            source_value = destination_value_sum
            if change_output:
                # add the difference between source and destination to the change
                change_value = change_outpM
ut[1] + (destination_value_sum - source_value)
                change_output = (change_output[0], change_value)
            unsigned_pretx = serializer.serialise_p2sh_pretx(inputs,
                                                             source=source_address,
                                                             source_value=source_value,
                                                             data_output=data_output,
                                                             change_output=changM
                                                             pubkey=dust_return_pubkey,
                                                             multisig_pubkeys=p2sh_source_multisig_pubkeys,
                                                             multisig_pubkeys_required=p2sh_source_multisig_pubkeys_required)
            unsigned_pretx_hex = binascii.hexlify(unsigned_pretx).decode('utf-8')
        # with segwit we already know the txid and can return both
            #pretx_M
txid = hashlib.sha256(unsigned_pretx).digest()  # this should be segwit txid
            ptx = CTransaction.stream_deserialize(io.BytesIO(unsigned_pretx)) # could be a non-segwit tx anyways
            txid_ba = bytearray(ptx.GetTxid())
            txid_ba.reverse()
            pretx_txid = bytes(txid_ba) # gonna leave the malleability problem to upstream
            logger.getChild('p2shdebug').debug('pretx_txid %s' % pretx_txid)
            print('pretx txid:', binascii.hexlify(pretx_txid))
        if unsigned_pM
            # we set a long lock on this, don't want other TXs to spend from it
            UTXO_P2SH_ENCODING_LOCKS[make_outkey_vin(unsigned_pretx, 0)] = True
        # only generate the data TX if we have the pretx txId
        if pretx_txid:
            source_input = None
            if script.is_p2sh(source):
                source_input = select_any_coin_from_source(source)
                if not source_input:
                    raise exceptions.TransactionError('Unable to select source input for p2shM
            unsigned_datatx = serializer.serialise_p2sh_datatx(pretx_txid,
                                                               source=source_address,
                                                               source_input=source_input,
                                                               destination_outputs=destination_outputs,
                                                               data_output=data_output,
                                                          M
     pubkey=dust_return_pubkey,
                                                               multisig_pubkeys=p2sh_source_multisig_pubkeys,
                                                               multisig_pubkeys_required=p2sh_source_multisig_pubkeys_required)
            unsigned_datatx_hex = binascii.hexlify(unsigned_datatx).decode('utf-8')
            # let the rest of the code work it's magic on the data tx
            unsigned_tx_hex = unsigned_datatx_hex
            # we're just gonna M
return the pretx, it doesn't require any of the further checks
            logger.warn('old_style_api = %s' % old_style_api)
            return return_result([unsigned_pretx_hex], old_style_api=old_style_api)
        # Serialise inputs and outputs.
        unsigned_tx = serializer.serialise(encoding, inputs, destination_outputs,
                                data_output, change_output,
                                dust_return_pubkey=dust_return_pubkey)
        unsigned_tx_hex = binascii.hexlify(unsiM
gned_tx).decode('utf-8')
    '''Sanity Check'''
    # Desired transaction info.
    (desired_source, desired_destination_outputs, desired_data) = tx_info
    desired_source = script.make_canonical(desired_source)
    desired_destination = script.make_canonical(desired_destination_outputs[0][0]) if desired_destination_outputs else ''
    # NOTE: Include change in destinations for BTC transactions.
    # if change_output and not desired_data and desired_destination != config.UNSPENDABLE:
    #    if desired_destinM
    #        desired_destination = desired_source
    #        desired_destination += '-{}'.format(desired_source)
    if desired_data == None:
        desired_data = b''
    # Parsed transaction info.
        if pretx_txid and unsigned_pretx:
            backend.cache_pretx(pretx_txid, unsigned_pretx)
        parsed_source, parsed_destination, x, y, parsed_data, extra = blocks._get_tx_info(unsigned_tx_hex, p2sh_is_segwit=script.is_bech32(desired_source))
 encoding == 'p2sh':
            # make_canonical can't determine the address, so we blindly change the desired to the parsed
            desired_source = parsed_source
        if pretx_txid and unsigned_pretx:
            backend.clear_pretx(pretx_txid)
    except exceptions.BTCOnlyError:
        if extended_tx_info:
            return {
                'btc_in': btc_in,
                'btc_out': destination_btc_out + data_btc_out,
                'btc_change': change_quanM
                'btc_fee': final_fee,
                'tx_hex': unsigned_tx_hex,
        logger.getChild('p2shdebug').debug('BTC-ONLY')
        return return_result([unsigned_pretx_hex, unsigned_tx_hex], old_style_api=old_style_api)
    desired_source = script.make_canonical(desired_source)
    # Check desired info against parsed info.
    desired = (desired_source, desired_destination, desired_data)
    parsed = (parsed_source, parsed_destination, parsed_data)
    if desired != parsed:
 # Unlock (revert) UTXO locks
        if UTXO_LOCKS is not None and inputs:
            for input in inputs:
                UTXO_LOCKS[source].pop(make_outkey(input), None)
        raise exceptions.TransactionError('Constructed transaction does not parse correctly: {}
 {}'.format(desired, parsed))
    if extended_tx_info:
            'btc_in': btc_in,
            'btc_out': destination_btc_out + data_btc_out,
            'btc_change': change_quantity,
            'btc_fee': final_fee,
     'tx_hex': unsigned_tx_hex,
    return return_result([unsigned_pretx_hex, unsigned_tx_hex], old_style_api=old_style_api)
def normalize_custom_inputs(raw_custom_inputs):
    custom_inputs = []
    for custom_input in raw_custom_inputs:
        if 'value' not in custom_input:
            custom_input['value'] = int(custom_input['amount'] * config.UNIT)
        custom_inputs.append(custom_input)
    return custom_inputs
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
logger = logging.getLogger(__name__)
from datetime import datetime
from dateutil.tz import tzlocal
from operator import itemgetter
import bitcoin as bitcoinlib
from counterpartylib.lib import exceptions
from counterpartylib.lib.exceptions import DecodeEM
from counterpartylib.lib import config
B26_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
# subasset contain only characters a-zA-Z0-9.-_@!
SUBASSET_DIGITS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_@!'
SUBASSET_REVERSE = {'a':1,'b':2,'c':3,'d':4,'e':5,'f':6,'g':7,'h':8,'i':9,'j':10,'k':11,'l':12,'m':13,'n':14,
                    'o':15,'p':16,'q':17,'r':18,'s':19,'t':20,'u':21,'v':22,'w':23,'x':24,'y':25,'z':26,
                    'A':27,'B':28,'C':29,'D':30,'E':31,'F':M
32,'G':33,'H':34,'I':35,'J':36,'K':37,'L':38,'M':39,
                    'N':40,'O':41,'P':42,'Q':43,'R':44,'S':45,'T':46,'U':47,'V':48,'W':49,'X':50,'Y':51,'Z':52,
                    '0':53,'1':54,'2':55,'3':56,'4':57,'5':58,'6':59,'7':60,'8':61,'9':62,'.':63,'-':64,'_':65,'@':66,'!':67}
# Obsolete in Python
3.4, with enum module.
BET_TYPE_NAME = {0: 'BullCFD', 1: 'BearCFD', 2: 'Equal', 3: 'NotEqual'}
BET_TYPE_ID = {'BullCFD': 0, 'BearCFD': 1, 'Equal': 2, 'NotEqual': 3}
json_dump = lambda x: json.dumps(x, sortM
_keys=True, indent=4)
json_print = lambda x: print(json_dump(x))
CURRENT_BLOCK_INDEX = None
CURR_DIR = os.path.dirname(os.path.realpath(__file__))
with open(CURR_DIR + '/../protocol_changes.json') as f:
    PROTOCOL_CHANGES = json.load(f)
class RPCError (Exception): pass
# TODO: Move to `util_test.py`.
t timeout properly. (If server hangs, then unhangs, no result.)
def api(method, params):
    """Poll API via JSON-RPC."""
    headers = {'content-type': 'application/json'}M
        "method": method,
        "params": params,
        "jsonrpc": "2.0",
    response = requests.post(config.RPC, data=json.dumps(payload), headers=headers)
    if response == None:
        raise RPCError('Cannot communicate with {} server.'.format(config.XCP_NAME))
    elif response.status_code != 200:
        if response.status_code == 500:
            raise RPCError('Malformed API call.')
            raise RPCError(str(response.status_code) + ' ' + respM
    response_json = response.json()
    if 'error' not in response_json.keys() or response_json['error'] == None:
            return response_json['result']
        except KeyError:
            raise RPCError(response_json)
        raise RPCError('{} ({})'.format(response_json['error']['message'], response_json['error']['code']))
    return [l[i:i + n] for i in range(0, len(l), n)]
    return [x for x in z]
    # Had to do it this way to support python 3.4, if we start
    # using the 3.5 runtime this can be replaced by:
    #  (first_elem, *t)
    l.insert(0, first_elem)
    it = itertools.groupby(l, itemgetter(0))
    for key, subiter in it:
       yield key, sum(item[1] for item in subiter)
def date_passed(date):
    """Check if the date has already passed."""
    return date <= int(time.time())
def price (numerator, denominator):
eturn price as Fraction or Decimal."""
    if CURRENT_BLOCK_INDEX >= 294500 or config.TESTNET or config.REGTEST: # Protocol change.
        return fractions.Fraction(numerator, denominator)
        numerator = D(numerator)
        denominator = D(denominator)
        return D(numerator / denominator)
def last_message(db):
    """Return latest message from the db."""
    cursor = db.cursor()
    messages = list(cursor.execute('''SELECT * FROM messages WHERE message_index = (SELECT MAX(message_index) from M
        assert len(messages) == 1
        last_message = messages[0]
        raise exceptions.DatabaseError('No messages found.')
    return last_message
def generate_asset_id(asset_name, block_index):
    """Create asset_id from asset_name."""
    if asset_name == config.BTC: return 0
    elif asset_name == config.XCP: return 1
    if len(asset_name) < 4:
        raise exceptions.AssetNameError('too short')
    # Numeric asset names.
umeric_asset_names'):  # Protocol change.
        if asset_name[0] == 'A':
            # Must be numeric.
                asset_id = int(asset_name[1:])
            except ValueError:
                raise exceptions.AssetNameError('non
numeric asset name starts with
            # Number must be in range.
            if not (26**12 + 1 <= asset_id <= 2**64 - 1):
                raise exceptions.AssetNameError('numeric asset name not in range')
            return asset_id
len(asset_name) >= 13:
            raise exceptions.AssetNameError('long asset names must be numeric')
    if asset_name[0] == 'A': raise exceptions.AssetNameError('non
numeric asset name starts with
    # Convert the Base 26 string to an integer.
    for c in asset_name:
        if c not in B26_DIGITS:
            raise exceptions.AssetNameError('invalid character:', c)
        digit = B26_DIGITS.index(c)
    if asset_id < 26**3:
raise exceptions.AssetNameError('too short')
def generate_asset_name (asset_id, block_index):
    """Create asset_name from asset_id."""
    if asset_id == 0: return config.BTC
    elif asset_id == 1: return config.XCP
    if asset_id < 26**3:
        raise exceptions.AssetIDError('too low')
    if enabled('numeric_asset_names'):  # Protocol change.
        if asset_id <= 2**64 - 1:
            if 26**12 + 1 <= asset_id:
                asset_name = 'A' + str(asset_id)
                returnM
            raise exceptions.AssetIDError('too high')
    # Divide that integer into Base 26 string.
        n, r = divmod (n, 26)
        res.append(B26_DIGITS[r])
    asset_name = ''.join(res[::-1])
    return asset_name + checksum.compute(asset_name)
    return asset_name
def get_asset_id (db, asset_name, block_index):
    """Return asset_id from asset_name."""
    if not enabled('hotfix_numeric_assets'):
generate_asset_id(asset_name, block_index)
    cursor = db.cursor()
    cursor.execute('''SELECT * FROM assets WHERE asset_name = ?''', (asset_name,))
    assets = list(cursor)
    if len(assets) == 1:
        return int(assets[0]['asset_id'])
        raise exceptions.AssetError('No such asset: {}'.format(asset_name))
def get_asset_name (db, asset_id, block_index):
    """Return asset_name from asset_id."""
    if not enabled('hotfix_numeric_assets'):
        return generate_asset_name(asset_id, block_inM
    cursor = db.cursor()
    cursor.execute('''SELECT * FROM assets WHERE asset_id = ?''', (str(asset_id),))
    assets = list(cursor)
    if len(assets) == 1:
        return assets[0]['asset_name']
    elif not assets:
        return 0    # Strange, I know
# If asset_name is an existing subasset (PARENT.child) then return the corresponding numeric asset name (A12345)
#   If asset_name is not an existing subasset, then return the unmodified asset_name
def resolve_subasset_longname(db, asset_name):
nabled('subassets'):
        subasset_longname = None
            subasset_parent, subasset_longname = parse_subasset_from_asset_name(asset_name)
        except Exception as e:
            logger.warn("Invalid subasset {}".format(asset_name))
            subasset_longname = None
        if subasset_longname is not None:
            cursor = db.cursor()
            cursor.execute('''SELECT asset_name FROM assets WHERE asset_longname = ?''', (subasset_longname,))
            assets = list(cursor)
      cursor.close()
            if len(assets) == 1:
                return assets[0]['asset_name']
    return asset_name
# checks and validates subassets (PARENT.SUBASSET)
#   throws exceptions for assset or subasset names with invalid syntax
#   returns (None, None) if the asset is not a subasset name
def parse_subasset_from_asset_name(asset):
    subasset_parent = None
    subasset_child = None
    subasset_longname = None
    chunks = asset.split('.', 1)
    if (len(chunks) == 2):
        subasset_parent = M
        subasset_child = chunks[1]
        subasset_longname = asset
        # validate parent asset
        validate_subasset_parent_name(subasset_parent)
        # validate child asset
        validate_subasset_longname(subasset_longname, subasset_child)
    return (subasset_parent, subasset_longname)
# throws exceptions for invalid subasset names
def validate_subasset_longname(subasset_longname, subasset_child=None):
    if subasset_child is None:
        chunks = subasset_longname.split('.', 1)
      if (len(chunks) == 2):
            subasset_child = chunks[1]
            subasset_child = ''
    if len(subasset_child) < 1:
        raise exceptions.AssetNameError('subasset name too short')
    if len(subasset_longname) > 250:
        raise exceptions.AssetNameError('subasset name too long')
    # can't start with period, can't have consecutive periods, can't contain anything not in SUBASSET_DIGITS
    previous_digit = '.'
    for c in subasset_child:
        if c not in SUBASSET_DIGITS:
          raise exceptions.AssetNameError('subasset name contains invalid character:', c)
        if c == '.' and previous_digit == '.':
            raise exceptions.AssetNameError('subasset name contains consecutive periods')
        previous_digit = c
    if previous_digit == '.':
        raise exceptions.AssetNameError('subasset name ends with a period')
# throws exceptions for invalid subasset names
def validate_subasset_parent_name(asset_name):
    if asset_name == config.BTC:
xceptions.AssetNameError('parent asset cannot be {}'.format(config.BTC))
    if asset_name == config.XCP:
        raise exceptions.AssetNameError('parent asset cannot be {}'.format(config.XCP))
    if len(asset_name) < 4:
        raise exceptions.AssetNameError('parent asset name too short')
    if len(asset_name) >= 13:
        raise exceptions.AssetNameError('parent asset name too long')
    if asset_name[0] == 'A':
        raise exceptions.AssetNameError('parent asset name starts with
        if c not in B26_DIGITS:
            raise exceptions.AssetNameError('parent asset name contains invalid character:', c)
def compact_subasset_longname(string):
    """Compacts a subasset name string into an array of bytes to save space using a base68 encoding scheme.
    Assumes all characters provided belong to SUBASSET_DIGITS.
    for i, c in enumerate(string[::-1]):
        name_int += (68 ** i) * SUBASSET_REVERSE[c]
    return name_int.to_bytes((name_int.M
bit_length() + 7) // 8, byteorder='big')
def expand_subasset_longname(raw_bytes):
    """Expands an array of bytes into a subasset name string."""
    integer = int.from_bytes(raw_bytes, byteorder='big')
    if integer == 0:
    while integer != 0:
        ret = SUBASSET_DIGITS[integer % 68 - 1] + ret
        integer //= 68
def generate_random_asset ():
    return 'A' + str(random.randint(26**12 + 1, 2**64 - 1))
def parse_options_from_string(string):
ions integer from string, if exists."""
    string_list = string.split(" ")
    if len(string_list) == 2:
            options = int(string_list.pop())
            raise exceptions.OptionsError('options not an integer')
        return options
        return False
def validate_address_options(options):
    """Ensure the options are all valid and in range."""
    if (options > config.MAX_INT) or (options < 0):
        raise exceptions.OptionsError('options integer overflow')
 elif options > config.ADDRESS_OPTION_MAX_VALUE:
        raise exceptions.OptionsError('options out of range')
    elif not active_options(config.ADDRESS_OPTION_MAX_VALUE, options):
        raise exceptions.OptionsError('options not possible')
def active_options(config, options):
    """Checks if options active in some given config."""
    return config & options == options
class DebitError (Exception): pass
def debit (db, address, asset, quantity, action=None, event=None):
    """Debit given address by quantity M
    block_index = CURRENT_BLOCK_INDEX
    if type(quantity) != int:
        raise DebitError('Quantity must be an integer.')
    if quantity < 0:
        raise DebitError('Negative quantity.')
    if quantity > config.MAX_INT:
        raise DebitError('Quantity can\'t be higher than MAX_INT.')
    if asset == config.BTC:
        raise DebitError('Cannot debit bitcoins.')
    debit_cursor = db.cursor()
    # Contracts can only hold XCP balances.
    if enabled('contracts_only_xcp_balances'): # ProtocM
        if len(address) == 40:
            assert asset == config.XCP
    if asset == config.BTC:
        raise exceptions.BalanceError('Cannot debit bitcoins from a {} address!'.format(config.XCP_NAME))
    debit_cursor.execute('''SELECT * FROM balances \
                            WHERE (address = ? AND asset = ?)''', (address, asset))
    balances = debit_cursor.fetchall()
    if not len(balances) == 1:
        old_balance = 0
        old_balance = balances[0]['quantity']
        raise DebitError('Insufficient funds.')
    balance = round(old_balance - quantity)
    balance = min(balance, config.MAX_INT)
    assert balance >= 0
        'quantity': balance,
        'address': address,
        'asset': asset
    sql='update balances set quantity = :quantity where (address = :address and asset = :asset)'
    debit_cursor.execute(sql, bindings)
        'block_index': block_index,
        'address': address,
       'asset': asset,
        'quantity': quantity,
        'action': action,
        'event': event
    sql='insert into debits values(:block_index, :address, :asset, :quantity, :action, :event)'
    debit_cursor.execute(sql, bindings)
    debit_cursor.close()
    BLOCK_LEDGER.append('{}{}{}{}'.format(block_index, address, asset, quantity))
class CreditError (Exception): pass
def credit (db, address, asset, quantity, action=None, event=None):
    """Credit given address by quantity of asset."""
index = CURRENT_BLOCK_INDEX
    if type(quantity) != int:
        raise CreditError('Quantity must be an integer.')
    if quantity < 0:
        raise CreditError('Negative quantity.')
    if quantity > config.MAX_INT:
        raise CreditError('Quantity can\'t be higher than MAX_INT.')
    if asset == config.BTC:
        raise CreditError('Cannot debit bitcoins.')
    credit_cursor = db.cursor()
    # Contracts can only hold XCP balances.
    if enabled('contracts_only_xcp_balances'): # Protocol change.
 if len(address) == 40:
            assert asset == config.XCP
    credit_cursor.execute('''SELECT * FROM balances \
                             WHERE (address = ? AND asset = ?)''', (address, asset))
    balances = credit_cursor.fetchall()
    if len(balances) == 0:
        assert balances == []
        #update balances table with new balance
        bindings = {
            'address': address,
            'asset': asset,
            'quantity': quantity,
        sql='insert into balances values(:addrM
ess, :asset, :quantity)'
        credit_cursor.execute(sql, bindings)
    elif len(balances) > 1:
        assert False
        old_balance = balances[0]['quantity']
        assert type(old_balance) == int
        balance = round(old_balance + quantity)
        balance = min(balance, config.MAX_INT)
        bindings = {
            'quantity': balance,
            'address': address,
            'asset': asset
        sql='update balances set quantity = :quantity where (address = :address and asM
        credit_cursor.execute(sql, bindings)
    # Record credit.
        'block_index': block_index,
        'address': address,
        'asset': asset,
        'quantity': quantity,
        'action': action,
        'event': event
    sql='insert into credits values(:block_index, :address, :asset, :quantity, :action, :event)'
    credit_cursor.execute(sql, bindings)
    credit_cursor.close()
    BLOCK_LEDGER.append('{}{}{}{}'.format(block_index, address, asset, quantity))
class QuantityError(Exception): pass
def is_divisible(db, asset):
    """Check if the asset is divisible."""
    if asset in (config.BTC, config.XCP):
        cursor = db.cursor()
        cursor.execute('''SELECT * FROM issuances \
                          WHERE (status = ? AND asset = ?) ORDER BY tx_index DESC''', ('valid', asset))
        issuances = cursor.fetchall()
        if not issuances: raise exceptions.AssetError('No such asset: {}'.format(asset))
        return issuances[M
def value_input(quantity, asset, divisible):
    if asset == 'leverage':
        return round(quantity)
    if asset in ('value', 'fraction', 'price', 'odds'):
        return float(quantity)  # TODO: Float?!
        quantity = D(quantity) * config.UNIT
        if quantity == quantity.to_integral():
            return int(quantity)
            raise QuantityError('Divisible assets have only eight decimal places of precision.')
        quantity = D(quantityM
        if quantity != round(quantity):
            raise QuantityError('Fractional quantities of indivisible assets.')
        return round(quantity)
def value_in(db, quantity, asset, divisible=None):
    if asset not in ['leverage', 'value', 'fraction', 'price', 'odds'] and divisible == None:
        divisible = is_divisible(db, asset)
    return value_input(quantity, asset, divisible)
def value_output(quantity, asset, divisible):
    def norm(num, places):
        """Round only if necessary."""
= round(num, places)
        fmt = '{:.' + str(places) + 'f}'
        num = fmt.format(num)
        return num.rstrip('0')+'0' if num.rstrip('0')[-1] == '.' else num.rstrip('0')
    if asset == 'fraction':
        return str(norm(D(quantity) * D(100), 6)) + '%'
    if asset in ('leverage', 'value', 'price', 'odds'):
        return norm(quantity, 6)
        quantity = D(quantity) / D(config.UNIT)
        if quantity == quantity.to_integral():
            return str(quantity) + '.0'  # For divisiM
ble assets, display the decimal point.
            return norm(quantity, 8)
        quantity = D(quantity)
        if quantity != round(quantity):
            raise QuantityError('Fractional quantities of indivisible assets.')
        return round(quantity)
def value_out(db, quantity, asset, divisible=None):
    if asset not in ['leverage', 'value', 'fraction', 'price', 'odds'] and divisible == None:
        divisible = is_divisible(db, asset)
    return value_output(quantity, asset, divisiM
def holders(db, asset, exclude_empty_holders=False):
    """Return holders of the asset."""
    cursor = db.cursor()
    if exclude_empty_holders:
        cursor.execute('''SELECT * FROM balances \
                          WHERE asset = ? AND quantity > ?''', (asset, 0))
        cursor.execute('''SELECT * FROM balances \
                          WHERE asset = ?''', (asset, ))
    for balance in list(cursor):
        holders.append({'address': balaM
nce['address'], 'address_quantity': balance['quantity'], 'escrow': None})
    # Funds escrowed in orders. (Protocol change.)
    cursor.execute('''SELECT * FROM orders \
                      WHERE give_asset = ? AND status = ?''', (asset, 'open'))
    for order in list(cursor):
        holders.append({'address': order['source'], 'address_quantity': order['give_remaining'], 'escrow': order['tx_hash']})
    # Funds escrowed in pending order matches. (Protocol change.)
    cursor.execute('''SELECT * FROM order_matcheM
                      WHERE (forward_asset = ? AND status = ?)''', (asset, 'pending'))
    for order_match in list(cursor):
        holders.append({'address': order_match['tx0_address'], 'address_quantity': order_match['forward_quantity'], 'escrow': order_match['id']})
    cursor.execute('''SELECT * FROM order_matches \
                      WHERE (backward_asset = ? AND status = ?)''', (asset, 'pending'))
    for order_match in list(cursor):
        holders.append({'address': order_match['tx1_address'], 'addreM
ss_quantity': order_match['backward_quantity'], 'escrow': order_match['id']})
    # Bets and RPS (and bet/rps matches) only escrow XCP.
    if asset == config.XCP:
        cursor.execute('''SELECT * FROM bets \
                          WHERE status = ?''', ('open',))
        for bet in list(cursor):
            holders.append({'address': bet['source'], 'address_quantity': bet['wager_remaining'], 'escrow': bet['tx_hash']})
        cursor.execute('''SELECT * FROM bet_matches \
                          WHERE statusM
 = ?''', ('pending',))
        for bet_match in list(cursor):
            holders.append({'address': bet_match['tx0_address'], 'address_quantity': bet_match['forward_quantity'], 'escrow': bet_match['id']})
            holders.append({'address': bet_match['tx1_address'], 'address_quantity': bet_match['backward_quantity'], 'escrow': bet_match['id']})
        cursor.execute('''SELECT * FROM rps \
                          WHERE status = ?''', ('open',))
        for rps in list(cursor):
            holders.append({'adM
dress': rps['source'], 'address_quantity': rps['wager'], 'escrow': rps['tx_hash']})
        cursor.execute('''SELECT * FROM rps_matches \
                          WHERE status IN (?, ?, ?)''', ('pending', 'pending and resolved', 'resolved and pending'))
        for rps_match in list(cursor):
            holders.append({'address': rps_match['tx0_address'], 'address_quantity': rps_match['wager'], 'escrow': rps_match['id']})
            holders.append({'address': rps_match['tx1_address'], 'address_quantity': rps_matcM
h['wager'], 'escrow': rps_match['id']})
    if enabled('dispensers_in_holders'):
        # Funds escrowed in dispensers.
        cursor.execute('''SELECT * FROM dispensers \
                          WHERE asset = ? AND status = ?''', (asset, 0))
        for dispenser in list(cursor):
            holders.append({'address': dispenser['source'], 'address_quantity': dispenser['give_remaining'], 'escrow': None})
def xcp_created (db):
    """Return number of XCP created thus faM
    cursor = db.cursor()
    cursor.execute('''SELECT SUM(earned) AS total FROM burns \
                      WHERE (status = ?)''', ('valid',))
    total = list(cursor)[0]['total'] or 0
def xcp_destroyed (db):
    """Return number of XCP destroyed thus far."""
    cursor = db.cursor()
    cursor.execute('''SELECT SUM(quantity) AS total FROM destructions \
                      WHERE (status = ? AND asset = ?)''', ('valid', config.XCP))
al = list(cursor)[0]['total'] or 0
    # Subtract issuance fees.
    cursor.execute('''SELECT SUM(fee_paid) AS total FROM issuances\
                      WHERE status = ?''', ('valid',))
    issuance_fee_total = list(cursor)[0]['total'] or 0
    # Subtract dividend fees.
    cursor.execute('''SELECT SUM(fee_paid) AS total FROM dividends\
                      WHERE status = ?''', ('valid',))
    dividend_fee_total = list(cursor)[0]['total'] or 0
    # Subtract sweep fees.
    cursor.execute('''SELECT SUM(fee_paid)M
 AS total FROM sweeps\
                      WHERE status = ?''', ('valid',))
    sweeps_fee_total = list(cursor)[0]['total'] or 0
    return destroyed_total + issuance_fee_total + dividend_fee_total + sweeps_fee_total
def xcp_supply (db):
    """Return the XCP supply."""
    return xcp_created(db) - xcp_destroyed(db)
    """Return creations."""
    cursor = db.cursor()
    creations = {config.XCP: xcp_created(db)}
    cursor.execute('''SELECT asset, SUM(quantity) AS createdM
                      WHERE status = ? GROUP BY asset''', ('valid',))
    for issuance in cursor:
        asset = issuance['asset']
        created = issuance['created']
        creations[asset] = created
    return creations
def destructions (db):
    """Return destructions."""
    cursor = db.cursor()
    destructions = {config.XCP: xcp_destroyed(db)}
    cursor.execute('''SELECT asset, SUM(quantity) AS destroyed FROM destructions \
                      WHERE (status = ? AM
ND asset != ?) GROUP BY asset''', ('valid', config.XCP))
    for destruction in cursor:
        asset = destruction['asset']
        destroyed = destruction['destroyed']
        destructions[asset] = destroyed
    return destructions
def asset_issued_total (db, asset):
    """Return asset total issued."""
    cursor = db.cursor()
    cursor.execute('''SELECT SUM(quantity) AS total FROM issuances \
                      WHERE (status = ? AND asset = ?)''', ('valid', asset))
    issued_total = lM
ist(cursor)[0]['total'] or 0
    return issued_total
def asset_destroyed_total (db, asset):
    """Return asset total destroyed."""
    cursor = db.cursor()
    cursor.execute('''SELECT SUM(quantity) AS total FROM destructions \
                      WHERE (status = ? AND asset = ?)''', ('valid', asset))
    destroyed_total = list(cursor)[0]['total'] or 0
    return destroyed_total
def asset_supply (db, asset):
    """Return asset supply."""
    return asset_issued_total(db, M
asset) - asset_destroyed_total(db, asset)
    """Return supplies."""
    d1 = creations(db)
    d2 = destructions(db)
    return {key: d1[key] - d2.get(key, 0) for key in d1.keys()}
def held (db): #TODO: Rename ?
        "SELECT asset, SUM(quantity) AS total FROM balances GROUP BY asset",
        "SELECT give_asset AS asset, SUM(give_remaining) AS total FROM orders WHERE status = 'open' GROUP BY asset",
        "SELECT give_asset AS asset, SUM(give_remaining) AS total FROM orderM
s WHERE status = 'filled' and give_asset = 'XCP' and get_asset = 'BTC' GROUP BY asset",
        "SELECT forward_asset AS asset, SUM(forward_quantity) AS total FROM order_matches WHERE status = 'pending' GROUP BY asset",
        "SELECT backward_asset AS asset, SUM(backward_quantity) AS total FROM order_matches WHERE status = 'pending' GROUP BY asset",
        "SELECT 'XCP' AS asset, SUM(wager_remaining) AS total FROM bets WHERE status = 'open'",
        "SELECT 'XCP' AS asset, SUM(forward_quantity) AS total FROM beM
t_matches WHERE status = 'pending'",
        "SELECT 'XCP' AS asset, SUM(backward_quantity) AS total FROM bet_matches WHERE status = 'pending'",
        "SELECT 'XCP' AS asset, SUM(wager) AS total FROM rps WHERE status = 'open'",
        "SELECT 'XCP' AS asset, SUM(wager * 2) AS total FROM rps_matches WHERE status IN ('pending', 'pending and resolved', 'resolved and pending')",
        "SELECT asset, SUM(give_remaining) AS total FROM dispensers WHERE status=0 OR status=1 GROUP BY asset",
    sql = "SELECT asM
set, SUM(total) AS total FROM (" + " UNION ALL ".join(queries) + ") GROUP BY asset;"
    cursor = db.cursor()
    cursor.execute(sql)
    for row in cursor:
        asset = row['asset']
        total = row['total']
        held[asset] = total
class GetURLError (Exception): pass
def get_url(url, abort_on_error=False, is_json=True, fetch_timeout=5):
    """Fetch URL using requests.get."""
        r = requests.get(url, timeout=fetch_timeout)
        raise GetURLError("Got get_url request error: %s" % e)
        if r.status_code != 200 and abort_on_error:
            raise GetURLError("Bad status code returned: '%s'. result body: '%s'." % (r.status_code, r.text))
        result = json.loads(r.text) if is_json else r.text
    if not isinstance(text, bytes):
        text = bytes(str(text), 'utf-8')
    return hashlib.sha256(hashlib.sha256(text).digest()).digest()
def dhash_string(text):
rn binascii.hexlify(dhash(text)).decode()
def get_balance (db, address, asset):
    """Get balance of contract or address."""
    cursor = db.cursor()
    balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (address, asset)))
    if not balances: return 0
    else: return balances[0]['quantity']
# Why on Earth does `binascii.hexlify()` return bytes?!
    """Return the hexadecimal representation of the binary data. Decode from ASCII to M
    return binascii.hexlify(x).decode('ascii')
def unhexlify(hex_string):
    return binascii.unhexlify(bytes(hex_string, 'utf-8'))
### Protocol Changes ###
def enabled(change_name, block_index=None):
    """Return True if protocol change is enabled."""
    if config.REGTEST:
        return True # All changes are always enabled on REGTEST
    if config.TESTNET:
        index_name = 'testnet_block_index'
        index_name = 'block_index'
    enable_block_index = PROTOCOL_CHANGES[change_name][M
    if not block_index:
        block_index = CURRENT_BLOCK_INDEX
    if block_index >= enable_block_index:
        return False
def get_value_by_block_index(change_name, block_index=None):
    if not block_index:
        block_index = CURRENT_BLOCK_INDEX
    if config.REGTEST:
        max_block_index_testnet = -1
        for key, value in PROTOCOL_CHANGES[change_name]["testnet"]:
            if int(key) > int(max_block_index):
                max_block_index = keyM
        return PROTOCOL_CHANGES[change_name]["testnet"][max_block_index]["value"]
    if config.TESTNET:
        index_name = 'testnet'
        index_name = 'mainnet'
    max_block_index = -1
    for key in PROTOCOL_CHANGES[change_name][index_name]:
        if int(key) > int(max_block_index) and block_index >= int(key):
            max_block_index = key
    return PROTOCOL_CHANGES[change_name][index_name][max_block_index]["value"]
def transfer(db, source, destinatM
ion, asset, quantity, action, event):
    """Transfer quantity of asset from source to destination."""
    debit(db, source, asset, quantity, action=action, event=event)
    credit(db, destination, asset, quantity, action=action, event=event)
def make_id(hash_1, hash_2):
    return hash_1 + ID_SEPARATOR + hash_2
def parse_id(match_id):
    assert match_id[64] == ID_SEPARATOR
    return match_id[:64], match_id[65:] # UTF-8 encoding means that the indices are doubled.
ance(v, dict) or isinstance(v, DictCache):
        for dk, dv in v.items():
            s += sizeof(dk)
            s += sizeof(dv)
        return sys.getsizeof(v)
    """Threadsafe FIFO dict cache"""
    def __init__(self, size=100):
        if int(size) < 1 :
            raise AttributeError('size < 1 or not a number')
        self.size = size
        self.dict = collections.OrderedDict()
        self.lock = threading.Lock()
    def __getitem__(self,keyM
        with self.lock:
            return self.dict[key]
    def __setitem__(self,key,value):
        with self.lock:
            while len(self.dict) >= self.size:
                self.dict.popitem(last=False)
            self.dict[key] = value
    def __delitem__(self,key):
        with self.lock:
            del self.dict[key]
    def __len__(self):
        with self.lock:
            return len(self.dict)
    def __contains__(self, key):
        with self.lock:
            return key in self.dict
ef refresh(self, key):
        with self.lock:
            self.dict.move_to_end(key, last=True)
URL_USERNAMEPASS_REGEX = re.compile('.+://(.+)@')
def clean_url_for_log(url):
    m = URL_USERNAMEPASS_REGEX.match(url)
    if m and m.group(1):
        url = url.replace(m.group(1), 'XXXXXXXX')
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
def satoshirate_to_fiat(satoshirate):
    return round(satoshirate/100.0,2)
def get_oracle_last_price(db, oracle_address, block_index):
    cursor.execute('SELECT * FROM broadcasts WHERE source=:source AND status=:status AND block_index<:block_index ORDER by tx_index DESC LIMIT 1', {
        'source': oracle_address,
        'status': 'valid',
        'block_index': block_index
    broadcasts = cursor.fetchall()
    if len(broadcasts) == 0:
        return None, None
    oracle_broadcast = broadcasts[0]
    oracle_label = oracle_broadcast["text"].split("-")
    if len(oracle_label) == 2:
fiat_label = oracle_label[1]
        fiat_label = ""
    return oracle_broadcast['value'], oracle_broadcast['fee_fraction_int'], fiat_label, oracle_broadcast['block_index']
#### util_windows.py
import logging.handlers
logger = logging.getLogger(__name__)
from ctypes import WINFUNCTYPE, windll, POINTER, byref, c_int
from ctypes.wintypes import BOOL, HANDLE, DWORD, LPWSTR, LPCWSTR, LPVOID
class SanitizedRotatingFileHanM
dler(logging.handlers.RotatingFileHandler):
    def emit(self, record):
        # If the message doesn't need to be rendered we take a shortcut.
        if record.levelno < self.level:
        # Make sure the message is a string.
        message = record.msg
        #Sanitize and clean up the message
        message = unicodedata.normalize('NFKD', message).encode('ascii', 'ignore').decode()
        # Copy the original record so we don't break other handlers.
        record = copy.copy(record)
     record.msg = message
        # Use the built-in stream handler to handle output.
        logging.handlers.RotatingFileHandler.emit(self, record)
def fix_win32_unicode():
    """Thanks to http://stackoverflow.com/a/3259271 ! (converted to python3)"""
    if sys.platform != "win32":
    original_stderr = sys.stderr
    # If any exception occurs in this code, we'll probably try to print it on stderr,
    # which makes for frustrating debugging if stderr is directed to our wrapper.
ranoid about catching errors and reporting them to original_stderr,
    # so that we can at least see them.
    def _complain(message):
        print(message if isinstance(message, str) else repr(message), file=original_stderr)
    # Work around <http://bugs.python.org/issue6058>.
    codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
    # Make Unicode console output work independently of the current code page.
    # This also fixes <http://bugs.python.org/issue1602>.
t to Michael Kaplan <http://blogs.msdn.com/b/michkap/archive/2010/04/07/9989346.aspx>
    # and TZOmegaTZIOY
    # <http://stackoverflow.com/questions/878972/windows-cmd-encoding-change-causes-python-crash/1432462#1432462>.
        # <http://msdn.microsoft.com/en-us/library/ms683231(VS.85).aspx>
        # HANDLE WINAPI GetStdHandle(DWORD nStdHandle);
        # returns INVALID_HANDLE_VALUE, NULL, or a valid handle
        # <http://msdn.microsoft.com/en-us/library/aa364960(VS.85).aspx>
WORD WINAPI GetFileType(DWORD hFile);
        # <http://msdn.microsoft.com/en-us/library/ms683167(VS.85).aspx>
        # BOOL WINAPI GetConsoleMode(HANDLE hConsole, LPDWORD lpMode);
        GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(("GetStdHandle", windll.kernel32))
        STD_OUTPUT_HANDLE = DWORD(-11)
        STD_ERROR_HANDLE = DWORD(-12)
        GetFileType = WINFUNCTYPE(DWORD, DWORD)(("GetFileType", windll.kernel32))
        FILE_TYPE_CHAR = 0x0002
        FILE_TYPE_REMOTE = 0x8000
eMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))(("GetConsoleMode", windll.kernel32))
        INVALID_HANDLE_VALUE = DWORD(-1).value
        def not_a_console(handle):
            if handle == INVALID_HANDLE_VALUE or handle is None:
                return True
            return ((GetFileType(handle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR
                    or GetConsoleMode(handle, byref(DWORD())) == 0)
        old_stdout_fileno = None
        old_stderr_fileno = None
        if hasattr(sys.stdout, 'fileno'):
            old_stdout_fileno = sys.stdout.fileno()
        if hasattr(sys.stderr, 'fileno'):
            old_stderr_fileno = sys.stderr.fileno()
        STDOUT_FILENO = 1
        STDERR_FILENO = 2
        real_stdout = (old_stdout_fileno == STDOUT_FILENO)
        real_stderr = (old_stderr_fileno == STDERR_FILENO)
        if real_stdout:
            hStdout = GetStdHandle(STD_OUTPUT_HANDLE)
            if not_a_console(hStdout):
                real_stdout = False
        if real_stderr:
            hStderr = GeM
tStdHandle(STD_ERROR_HANDLE)
            if not_a_console(hStderr):
                real_stderr = False
        if real_stdout or real_stderr:
            # BOOL WINAPI WriteConsoleW(HANDLE hOutput, LPWSTR lpBuffer, DWORD nChars,
            #                           LPDWORD lpCharsWritten, LPVOID lpReserved);
            WriteConsoleW = WINFUNCTYPE(BOOL, HANDLE, LPWSTR, DWORD, POINTER(DWORD), LPVOID)(("WriteConsoleW", windll.kernel32))
            class UnicodeOutput:
                def __init__(self, hConsoM
le, stream, fileno, name):
                    self._hConsole = hConsole
                    self._stream = stream
                    self._fileno = fileno
                    self.closed = False
                    self.softspace = False
                    self.mode = 'w'
                    self.encoding = 'utf-8'
                    self.name = name
                    self.errors = ''
                    self.flush()
                def isatty(self):
                    return False
                def closM
                    # don't really close the handle, that would only cause problems
                    self.closed = True
                def fileno(self):
                    return self._fileno
                def flush(self):
                    if self._hConsole is None:
                        try:
                            self._stream.flush()
                        except Exception as e:
                            _complain("%s.flush: %r from %r" % (self.name, e, self._stream))
                def write(self, text):
                    try:
                        if self._hConsole is None:
                            if isinstance(text, str):
                                text = text.encode('utf-8')
                            self._stream.write(text)
                        else:
                            if not isinstance(text, str):
                                text = str(text).decode('utf-8')
                            remaining = len(text)
              while remaining:
                                n = DWORD(0)
                                # There is a shorter-than-documented limitation on the
                                # length of the string passed to WriteConsoleW (see
                                # <http://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232>.
                                retval = WriteConsoleW(self._hConsole, text, min(remaining, 10000), byref(n), None)
                                if retval == 0 or n.value == 0:
                              raise IOError("WriteConsoleW returned %r, n.value = %r" % (retval, n.value))
                                remaining -= n.value
                                if not remaining:
                                    break
                                text = text[n.value:]
                    except Exception as e:
                        _complain("%s.write: %r" % (self.name, e))
                        raise
                def writelines(self, lines):
                    try:
                     for line in lines:
                            self.write(line)
                    except Exception as e:
                        _complain("%s.writelines: %r" % (self.name, e))
                        raise
            if real_stdout:
                sys.stdout = UnicodeOutput(hStdout, None, STDOUT_FILENO, '<Unicode console stdout>')
                sys.stdout = UnicodeOutput(None, sys.stdout, old_stdout_fileno, '<Unicode redirected stdout>')
            if real_stderr:
              sys.stderr = UnicodeOutput(hStderr, None, STDERR_FILENO, '<Unicode console stderr>')
                sys.stderr = UnicodeOutput(None, sys.stderr, old_stderr_fileno, '<Unicode redirected stderr>')
    except Exception as e:
        _complain("exception %r while fixing up sys.stdout and sys.stderr" % (e,))
    # While we're at it, let's unmangle the command-line arguments:
    # This works around <http://bugs.python.org/issue2128>.
    GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommM
andLineW", windll.kernel32))
    CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))(("CommandLineToArgvW", windll.shell32))
    argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc))
    argv = [argv_unicode[i].encode('utf-8').decode('utf-8') for i in range(0, argc.value)]
    if not hasattr(sys, 'frozen'):
        # If this is an executable produced by py2exe or bbfreeze, then it will
        # have been invoked directly. Otherwise, unicode_argv[0] is the PM
        # interpreter, so skip that.
        argv = argv[1:]
        # Also skip option arguments to the Python interpreter.
        while len(argv) > 0:
            arg = argv[0]
            if not arg.startswith("-") or arg == "-":
                break
            argv = argv[1:]
            if arg == '-m':
                # sys.argv[0] should really be the absolute path of the module source,
                # but never mind
                break
            if arg == '-c':
                argv[0] = '-c'
                break
#### addrindexrs.py
logger = logging.getLogger(__name__)
from requests.exceptions import Timeout, ReadTimeout, ConnectionError
import concurrent.futures
import bitcoin.wallet
from counterpartylib.lib import config, util, address
SOCKET_TIMEOUT = 5.0
BACKEND_PING_TIME = 30.0
raw_transactions_cache = util.DictCache(size=config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE)  # used in getrawtransaction_batch()
class BackendRPCError(Exception):
class AddrIndexRsRPCError(Exception):
def rpc_call(payload):
    """Calls to bitcoin core and returns the response"""
    url = config.BACKEND_URL
    for i in range(TRIES):
            response = requests.post(url, data=json.dumps(payloaM
d), headers={'content-type': 'application/json'},
                verify=(not config.BACKEND_SSL_NO_VERIFY), timeout=config.REQUESTS_TIMEOUT)
            if i > 0:
                logger.debug('Successfully connected.')
        except (Timeout, ReadTimeout, ConnectionError):
            logger.debug('Could not connect to backend at `{}`. (Try {}/{})'.format(util.clean_url_for_log(url), i+1, TRIES))
            time.sleep(5)
    if response == None:
        if config.TESTNET:
            network =M
        elif config.REGTEST:
            network = 'regtest'
            network = 'mainnet'
        raise BackendRPCError('Cannot communicate with backend at `{}`. (server is set to run on {}, is backend?)'.format(util.clean_url_for_log(url), network))
    elif response.status_code in (401,):
        raise BackendRPCError('Authorization error connecting to {}: {} {}'.format(util.clean_url_for_log(url), response.status_code, response.reason))
    elif response.status_code not in (200, 500):M
        raise BackendRPCError(str(response.status_code) + ' ' + response.reason)
    # Handle json decode errors
        response_json = response.json()
    except json.decoder.JSONDecodeError as e:
        raise BackendRPCError('Received invalid JSON from backend with a response of {}'.format(str(response.status_code) + ' ' + response.reason))
    # Batch query returns a list
    if isinstance(response_json, list):
        return response_json
    if 'error' not in response_json.keys() or response_jsonM
        return response_json['result']
    elif response_json['error']['code'] == -5:   # RPC_INVALID_ADDRESS_OR_KEY
        raise BackendRPCError('{} Is `txindex` enabled in {} Core?'.format(response_json['error'], config.BTC_NAME))
    elif response_json['error']['code'] in [-28, -8, -2]:
Block height out of range
The network does not appear to fully agree!
        logger.debug('Backend not ready. Sleeping for ten seconds.')
coin Core takes more than `sys.getrecursionlimit() * 10 = 9970`
        # seconds to start, this
ll hit the maximum recursion depth limit.
        time.sleep(10)
        return rpc_call(payload)
        raise BackendRPCError('Error connecting to {}: {}'.format(util.clean_url_for_log(url), response_json['error']))
def rpc(method, params):
        "method": method,
        "params": params,
        "jsonrpc": "2.0",
    return rpc_call(payload)
def rpc_batch(requesM
    responses = collections.deque()
    def make_call(chunk):
        #send a list of requests to bitcoind to be executed
        #note that this is list executed serially, in the same thread in bitcoind
        #e.g. see: https://github.com/bitcoin/bitcoin/blob/master/src/rpcserver.cpp#L939
        responses.extend(rpc_call(chunk))
    chunks = util.chunkify(request_list, config.RPC_BATCH_SIZE)
    with concurrent.futures.ThreadPoolExecutor(max_workers=config.BACKEND_RPC_BATCH_NUM_WORKERS) as executor:
        for chunk in chunks:
            executor.submit(make_call, chunk)
    return list(responses)
def extract_addresses(txhash_list):
    logger.debug('extract_addresses, txs: %d' % (len(txhash_list), ))
    tx_hashes_tx = getrawtransaction_batch(txhash_list, verbose=True)
    return extract_addresses_from_txlist(tx_hashes_tx, getrawtransaction_batch)
def extract_addresses_from_txlist(tx_hashes_tx, _getrawtransaction_batch):
    helper for extract_addresses, seperated so we can pass in a mocked _getrM
awtransaction_batch for test purposes
    logger.debug('extract_addresses_from_txlist, txs: %d' % (len(tx_hashes_tx.keys()), ))
    tx_hashes_addresses = {}
    tx_inputs_hashes = set()  # use set to avoid duplicates
    for tx_hash, tx in tx_hashes_tx.items():
        tx_hashes_addresses[tx_hash] = set()
        for vout in tx['vout']:
            if 'addresses' in vout['scriptPubKey']:
                tx_hashes_addresses[tx_hash].update(tuple(vout['scriptPubKey']['addresses']))
        tx_inputs_hashesM
.update([vin['txid'] for vin in tx['vin']])
    logger.debug('extract_addresses, input TXs: %d' % (len(tx_inputs_hashes), ))
    # chunk txs to avoid huge memory spikes
    for tx_inputs_hashes_chunk in util.chunkify(list(tx_inputs_hashes), config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE):
        raw_transactions = _getrawtransaction_batch(tx_inputs_hashes_chunk, verbose=True)
        for tx_hash, tx in tx_hashes_tx.items():
            for vin in tx['vin']:
                vin_tx = raw_transactions.get(vin['txid'], M
                if not vin_tx:
                    continue
                vout = vin_tx['vout'][vin['vout']]
                if 'addresses' in vout['scriptPubKey']:
                    tx_hashes_addresses[tx_hash].update(tuple(vout['scriptPubKey']['addresses']))
    return tx_hashes_addresses, tx_hashes_tx
def getblockcount():
    return rpc('getblockcount', [])
def getblockhash(blockcount):
    return rpc('getblockhash', [blockcount])
def getblock(block_hash):
    return rpc('getblock', [block_hash, M
def getrawtransaction(tx_hash, verbose=False, skip_missing=False):
    return getrawtransaction_batch([tx_hash], verbose=verbose, skip_missing=skip_missing)[tx_hash]
def getrawmempool():
    return rpc('getrawmempool', [])
def fee_per_kb(conf_target, mode, nblocks=None):
    :param conf_target:
    :return: fee_per_kb in satoshis, or None when unable to determine
    if nblocks is None and conf_target is None:
        conf_target = nblocks
    feeperkb = rpc('estimatesmaM
rtfee', [conf_target, mode])
    if 'errors' in feeperkb and feeperkb['errors'][0] == 'Insufficient data or no feerate found':
    return int(max(feeperkb['feerate'] * config.UNIT, config.DEFAULT_FEE_PER_KB_ESTIMATE_SMART))
def sendrawtransaction(tx_hex):
    return rpc('sendrawtransaction', [tx_hex])
GETRAWTRANSACTION_MAX_RETRIES=2
monotonic_call_id = 0
def getrawtransaction_batch(txhash_list, verbose=False, skip_missing=False, _retry=0):
    _logger = logger.getChild("getrawtransaction_batM
    if len(txhash_list) > config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE:
        #don't try to load in more than BACKEND_RAW_TRANSACTIONS_CACHE_SIZE entries in a single call
        txhash_list_chunks = util.chunkify(txhash_list, config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE)
        for txhash_list_chunk in txhash_list_chunks:
            txes.update(getrawtransaction_batch(txhash_list_chunk, verbose=verbose, skip_missing=skip_missing))
    tx_hash_call_id = {}
    noncached_txhashes = set()
    txhash_list = set(txhash_list)
    # payload for transactions not in cache
    for tx_hash in txhash_list:
        if tx_hash not in raw_transactions_cache:
            #call_id = binascii.hexlify(os.urandom(5)).decode('utf8') # Don't drain urandom
            global monotonic_call_id
            monotonic_call_id = monotonic_call_id + 1
            call_id = "{}".format(monotonic_call_id)
            payload.append({
                "method": 'getrawtransaction',
     "params": [tx_hash, 1],
                "jsonrpc": "2.0",
                "id": call_id
            noncached_txhashes.add(tx_hash)
            tx_hash_call_id[call_id] = tx_hash
    #refresh any/all cache entries that already exist in the cache,
    # so they're not inadvertently removed by another thread before we can consult them
    #(this assumes that the size of the working set for any given workload doesn't exceed the max size of the cache)
    for tx_hash in txhash_list.difference(noncacM
        raw_transactions_cache.refresh(tx_hash)
    _logger.debug("getrawtransaction_batch: txhash_list size: {} / raw_transactions_cache size: {} / # getrawtransaction calls: {}".format(
        len(txhash_list), len(raw_transactions_cache), len(payload)))
    # populate cache
    if len(payload) > 0:
        batch_responses = rpc_batch(payload)
        for response in batch_responses:
            if 'error' not in response or response['error'] is None:
                tx_hex = response['result']
                tx_hash = tx_hash_call_id[response['id']]
                raw_transactions_cache[tx_hash] = tx_hex
            elif skip_missing and 'error' in response and response['error']['code'] == -5:
                raw_transactions_cache[tx_hash] = None
                logging.debug('Missing TX with no raw info skipped (txhash: {}): {}'.format(
                    tx_hash_call_id.get(response.get('id', '??'), '??'), response['error']))
                #TODO: this seems to happen for bogus tM
ransactions? Maybe handle it more gracefully than just erroring out?
                raise BackendRPCError('{} (txhash:: {})'.format(response['error'], tx_hash_call_id.get(response.get('id', '??'), '??')))
    # get transactions from cache
    for tx_hash in txhash_list:
            if verbose:
                result[tx_hash] = raw_transactions_cache[tx_hash]
                result[tx_hash] = raw_transactions_cache[tx_hash]['hex'] if raw_transactions_cache[tx_hash] isM
        except KeyError as e: #shows up most likely due to finickyness with addrindex not always returning results that we need...
            print("Key error in addrindexrs still exists!!!!!")
            _logger.warning("tx missing in rawtx cache: {} -- txhash_list size: {}, hash: {} / raw_transactions_cache size: {} / # rpc_batch calls: {} / txhash in noncached_txhashes: {} / txhash in txhash_list: {} -- list {}".format(
                e, len(txhash_list), hashlib.md5(json.dumps(list(txhashM
_list)).encode()).hexdigest(), len(raw_transactions_cache), len(payload),
                tx_hash in noncached_txhashes, tx_hash in txhash_list, list(txhash_list.difference(noncached_txhashes)) ))
            if  _retry < GETRAWTRANSACTION_MAX_RETRIES: #try again
                time.sleep(0.05 * (_retry + 1)) # Wait a bit, hitting the index non-stop may cause it to just break down... TODO: Better handling
                r = getrawtransaction_batch([tx_hash], verbose=verbose, skip_missing=skip_missing, _retry=_retM
                result[tx_hash] = r[tx_hash]
                raise #already tried again, give up
class AddrIndexRsThread (threading.Thread):
    def __init__(self, host, port):
        threading.Thread.__init__(self)
        self.host = host
        self.port = port
        self.sock = None
        self.lastId = 0
        self.message_to_send = None
        self.message_result = None
        self.is_killed = False
        logging.debug('AddrIndexRs thM
        self.send({"kill": True})
    def connect(self):
        self.lastId = 0
            logging.info('AddrIndexRs connecting...')
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.settimeout(SOCKET_TIMEOUT)
                self.sock.connect((self.host, self.port))
                logging.info('Error connecting to AddrIndexRs! Retrying in a few seconds')
                time.sleep(5.0)
                logging.info('Connected to AddrIndexRs!')
                break
        self.locker = threading.Condition()
        self.locker.acquire()
        self.connect()
        while self.locker.wait():
            if not(self.is_killed) and self.message_to_send != None:
                msg = self.message_to_send
                self.message_to_send = None
                retry_count = 15
                while retry_count > 0:
                    has_sent = False
      while not(has_sent) and msg:
                        try:
                            logging.debug('AddrIndexRs sending')
                            self.sock.send(msg)
                            has_sent = True
                        except Exception as e:
                            #try:
                            logging.debug('AddrIndexRs error:' + e)
                            self.connect()
                            #except Exception as e2:
                            #logging.debug('AddrIndexRM
s fatal error:' + e2)
                    self.message_to_send = None
                    data = b""
                    parsed = False
                    while not(parsed):
                        try:
                            data = data + self.sock.recv(READ_BUF_SIZE)
                            self.message_result = json.loads(data.decode('utf-8'))
                            retry_count = 0
                            parsed = True
                            logging.debug('AddrIndexRs Recv complete!')
                      except socket.timeout:
                            logging.debug('AddrIndexRs Recv timeout error sending: '+str(msg))
                            if retry_count <= 0:
                                self.connect()
                            self.message_result = None
                            retry_count -= -1
                        except socket.error as e:
                            logging.debug('AddrIndexRs Recv error:' + str(e)+' with msg '+str(msg))
                            self.M
                        except Exception as e:
                            logging.debug('AddrIndexRs Recv error:' + str(e)+' with msg '+str(msg))
                            if retry_count <= 0:
                                raise e
                            self.message_result = None
                            retry_count -= 1
                        finally:
                            self.locker.notify()
                self.locker.notify()
        self.sock.close()
ging.debug('AddrIndexRs socket closed normally')
    def send(self, msg):
        self.locker.acquire()
        if not("kill" in msg):
            msg["id"] = self.lastId
            self.lastId += 1
            self.message_to_send = (json.dumps(msg) + "\n").encode('utf8')
        self.locker.notify()
        self.locker.wait()
        self.locker.release()
        return self.message_result
def ensure_addrindexrs_connected():
    max_backoff = 5000
le _backend == None:
            _backend = AddrIndexRsThread(config.INDEXD_CONNECT, config.INDEXD_PORT)
            _backend.daemon = True
            _backend.start()
            _backend.send({
                "method": "server.version",
                "params": []
        except Exception as e:
            logger.debug(e)
            time.sleep(backoff)
            backoff = min(backoff * 1.5, max_backoff)
def _script_pubkey_to_hash(spk):
    return hashlib.sha256(spk).digest()[::M
def _address_to_hash(addr):
    script_pubkey = bitcoin.wallet.CBitcoinAddress(addr).to_scriptPubKey()
    return _script_pubkey_to_hash(script_pubkey)
# Returns an array of UTXOS from an address in the following format
#   "txId": utxo_txid_hex,
#   "confirmations": num
# [{"txId":"a0d12eb3716e2e70fd00525486ace0da2947f82d818b7be0285f16ff672cf237","vout":5,"height":647484,"value":30455293,"confirmations":2}]
def unpack_outpoint(outpoint):
    txid, vout = outpoint.split(':')
    return (txid, int(vout))
def unpack_vout(outpoint, tx, block_count):
    vout = tx["vout"][outpoint[1]]
    if "confirmations" in tx and tx["confirmations"] > 0:
        height = block_count - tx["confirmations"] + 1
        tx["confirmations"] = 0
        "txId": tx["txid"],
        "vout": outpoint[1],
        "height": height,
        "value": int(round(vout["value"] * config.UNIT)),
"confirmations": tx["confirmations"]
def get_unspent_txouts(source):
    ensure_addrindexrs_connected()
    block_count = getblockcount()
    result = _backend.send({
        "method": "blockchain.scripthash.get_utxos",
        "params": [_address_to_hash(source)]
    if not(result is None) and "result" in result:
        result = result["result"]
        result = [unpack_outpoint(x) for x in result]
        # each item on the result array is like
        # {"tx_hash": hex_encoded_hash}
h = getrawtransaction_batch([x[0] for x in result], verbose=True, skip_missing=True)
        batch = [unpack_vout(outpoint, batch[outpoint[0]], block_count) for outpoint in result if outpoint[0] in batch]
        batch = [x for x in batch if x is not None]
        return batch
# Returns transactions in the following format
#  "blockhash": hexstring,
#  "blocktime": num,
#  "confirmations": num,
#  "hash": hexstring,
#  "hex": hexstring,
#  "txid": hexstring,
#      "txinwitness": array of hex_witness_program, // Only if it's a witness-containing tx
#      "txid": hexstring,
#      "sequence": num,
#      "coinbase": X, // contents not important, this is only present if the tx is a coinbase
#      "scriptSig": {
#        "asm": asm_decompiled_program,
#        "hex": hex_program
#       "value": decimal,
#       "scriptPubKey": {
#           "type": string,
#           "reqSigs": num,
#           "hex": hexstring, // the program in hex
#           "asm": string, // the decompiled program
#           "addresses": [ ...list of found addresses on the program ]
def search_raw_transactions(address, unconfirmed=True):
    ensure_addrindexrs_connected()
    hsh = _address_to_hash(address)
    txs = _backend.send({
        "method": "blockchain.scripthash.getM
        "params": [hsh]
    batch = getrawtransaction_batch([x["tx_hash"] for x in txs], verbose=True)
    if not(unconfirmed):
        batch = [x for x in batch if x.height >= 0]
# Returns the number of blocks the backend is behind the node
def getindexblocksbehind():
    # Addrindexrs never "gets behind"
    ensure_addrindexrs_connected()
    if '_backend' in globals():
        _backend.stop()
logger = logging.getLogger(__name__)
from requests.exceptions import Timeout, ReadTimeout, ConnectionError
import concurrent.futures
from counterpartylib.lib import config, util
raw_transactions_cache = util.DictCache(size=config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE)  # used in getrawtransaction_batch()
class BackendRPCError(Exception):
class IndexdRPCError(Exception):M
def rpc_call(payload):
    """Calls to bitcoin core and returns the response"""
    url = config.BACKEND_URL
    for i in range(TRIES):
            response = requests.post(url, data=json.dumps(payload), headers={'content-type': 'application/json'},
                verify=(not config.BACKEND_SSL_NO_VERIFY), timeout=config.REQUESTS_TIMEOUT)
            if i > 0:
                logger.debug('Successfully connected.')
        except (TimeoM
ut, ReadTimeout, ConnectionError):
            logger.debug('Could not connect to backend at `{}`. (Try {}/{})'.format(util.clean_url_for_log(url), i+1, TRIES))
            time.sleep(5)
    if response == None:
        if config.TESTNET:
            network = 'testnet'
        elif config.REGTEST:
            network = 'regtest'
            network = 'mainnet'
        raise BackendRPCError('Cannot communicate with backend at `{}`. (server is set to run on {}, is backend?)'.format(util.clean_url_for_M
    elif response.status_code in (401,):
        raise BackendRPCError('Authorization error connecting to {}: {} {}'.format(util.clean_url_for_log(url), response.status_code, response.reason))
    elif response.status_code not in (200, 500):
        raise BackendRPCError(str(response.status_code) + ' ' + response.reason)
    # Handle json decode errors
        response_json = response.json()
    except json.decoder.JSONDecodeError as e:
        raise BackendRPCError('Received invalid JM
SON from backend with a response of {}'.format(str(response.status_code) + ' ' + response.reason))
    # Batch query returns a list
    if isinstance(response_json, list):
        return response_json
    if 'error' not in response_json.keys() or response_json['error'] == None:
        return response_json['result']
    elif response_json['error']['code'] == -5:   # RPC_INVALID_ADDRESS_OR_KEY
        raise BackendRPCError('{} Is `txindex` enabled in {} Core?'.format(response_json['error'], config.BTC_NAME))
if response_json['error']['code'] in [-28, -8, -2]:
Block height out of range
The network does not appear to fully agree!
        logger.debug('Backend not ready. Sleeping for ten seconds.')
        # If Bitcoin Core takes more than `sys.getrecursionlimit() * 10 = 9970`
        # seconds to start, this
ll hit the maximum recursion depth limit.
        time.sleep(10)
        return rpc_call(payload)
        raise BackendRPCError('Error connecting to {M
}: {}'.format(util.clean_url_for_log(url), response_json['error']))
def rpc(method, params):
        "method": method,
        "params": params,
        "jsonrpc": "2.0",
    return rpc_call(payload)
def rpc_batch(request_list):
    responses = collections.deque()
    def make_call(chunk):
        #send a list of requests to bitcoind to be executed
        #note that this is list executed serially, in the same thread in bitcoind
        #e.g. see: https://github.com/bitcoinM
/bitcoin/blob/master/src/rpcserver.cpp#L939
        responses.extend(rpc_call(chunk))
    chunks = util.chunkify(request_list, config.RPC_BATCH_SIZE)
    with concurrent.futures.ThreadPoolExecutor(max_workers=config.BACKEND_RPC_BATCH_NUM_WORKERS) as executor:
        for chunk in chunks:
            executor.submit(make_call, chunk)
    return list(responses)
def extract_addresses(txhash_list):
    logger.debug('extract_addresses, txs: %d' % (len(txhash_list), ))
    tx_hashes_tx = getrawtransaction_batch(txhashM
_list, verbose=True)
    return extract_addresses_from_txlist(tx_hashes_tx, getrawtransaction_batch)
def extract_addresses_from_txlist(tx_hashes_tx, _getrawtransaction_batch):
    helper for extract_addresses, seperated so we can pass in a mocked _getrawtransaction_batch for test purposes
    logger.debug('extract_addresses_from_txlist, txs: %d' % (len(tx_hashes_tx.keys()), ))
    tx_hashes_addresses = {}
    tx_inputs_hashes = set()  # use set to avoid duplicates
    for tx_hash, tx in tx_hashM
        tx_hashes_addresses[tx_hash] = set()
        for vout in tx['vout']:
            if 'addresses' in vout['scriptPubKey']:
                tx_hashes_addresses[tx_hash].update(tuple(vout['scriptPubKey']['addresses']))
        tx_inputs_hashes.update([vin['txid'] for vin in tx['vin']])
    logger.debug('extract_addresses, input TXs: %d' % (len(tx_inputs_hashes), ))
    # chunk txs to avoid huge memory spikes
    for tx_inputs_hashes_chunk in util.chunkify(list(tx_inputs_hashes), config.BACKENDM
_RAW_TRANSACTIONS_CACHE_SIZE):
        raw_transactions = _getrawtransaction_batch(tx_inputs_hashes_chunk, verbose=True)
        for tx_hash, tx in tx_hashes_tx.items():
            for vin in tx['vin']:
                vin_tx = raw_transactions.get(vin['txid'], None)
                if not vin_tx:
                    continue
                vout = vin_tx['vout'][vin['vout']]
                if 'addresses' in vout['scriptPubKey']:
                    tx_hashes_addresses[tx_hash].update(tuple(vout['scriptPubKey']M
    return tx_hashes_addresses, tx_hashes_tx
def getblockcount():
    return rpc('getblockcount', [])
def getblockhash(blockcount):
    return rpc('getblockhash', [blockcount])
def getblock(block_hash):
    return rpc('getblock', [block_hash, False])
def getrawtransaction(tx_hash, verbose=False, skip_missing=False):
    return getrawtransaction_batch([tx_hash], verbose=verbose, skip_missing=skip_missing)[tx_hash]
def getrawmempool():
    return rpc('getrawmempool', [])
def fee_per_kb(conf_tarM
get, mode, nblocks=None):
    :param conf_target:
    :return: fee_per_kb in satoshis, or None when unable to determine
    if nblocks is None and conf_target is None:
        conf_target = nblocks
    feeperkb = rpc('estimatesmartfee', [conf_target, mode])
    if 'errors' in feeperkb and feeperkb['errors'][0] == 'Insufficient data or no feerate found':
    return int(max(feeperkb['feerate'] * config.UNIT, config.DEFAULT_FEE_PER_KB_ESTIMATE_SMART))
wtransaction(tx_hex):
    return rpc('sendrawtransaction', [tx_hex])
GETRAWTRANSACTION_MAX_RETRIES=2
def getrawtransaction_batch(txhash_list, verbose=False, skip_missing=False, _retry=0):
    _logger = logger.getChild("getrawtransaction_batch")
    if len(txhash_list) > config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE:
        #don't try to load in more than BACKEND_RAW_TRANSACTIONS_CACHE_SIZE entries in a single call
        txhash_list_chunks = util.chunkify(txhash_list, config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE)
        for txhash_list_chunk in txhash_list_chunks:
            txes.update(getrawtransaction_batch(txhash_list_chunk, verbose=verbose, skip_missing=skip_missing))
    tx_hash_call_id = {}
    noncached_txhashes = set()
    txhash_list = set(txhash_list)
    # payload for transactions not in cache
    for tx_hash in txhash_list:
        if tx_hash not in raw_transactions_cache:
            call_id = binascii.hexlify(os.urandom(5)).decode('utf8')
                "method": 'getrawtransaction',
                "params": [tx_hash, 1],
                "jsonrpc": "2.0",
                "id": call_id
            noncached_txhashes.add(tx_hash)
            tx_hash_call_id[call_id] = tx_hash
    #refresh any/all cache entries that already exist in the cache,
    # so they're not inadvertently removed by another thread before we can consult them
    #(this assumes that the size of the working set for any given workload doesn't exceed tM
he max size of the cache)
    for tx_hash in txhash_list.difference(noncached_txhashes):
        raw_transactions_cache.refresh(tx_hash)
    _logger.debug("getrawtransaction_batch: txhash_list size: {} / raw_transactions_cache size: {} / # getrawtransaction calls: {}".format(
        len(txhash_list), len(raw_transactions_cache), len(payload)))
    # populate cache
    if len(payload) > 0:
        batch_responses = rpc_batch(payload)
        for response in batch_responses:
            if 'error' not in response M
or response['error'] is None:
                tx_hex = response['result']
                tx_hash = tx_hash_call_id[response['id']]
                raw_transactions_cache[tx_hash] = tx_hex
            elif skip_missing and 'error' in response and response['error']['code'] == -5:
                raw_transactions_cache[tx_hash] = None
                logging.debug('Missing TX with no raw info skipped (txhash: {}): {}'.format(
                    tx_hash_call_id.get(response.get('id', '??'), '??'), response['error']))M
                #TODO: this seems to happen for bogus transactions? Maybe handle it more gracefully than just erroring out?
                raise BackendRPCError('{} (txhash:: {})'.format(response['error'], tx_hash_call_id.get(response.get('id', '??'), '??')))
    # get transactions from cache
    for tx_hash in txhash_list:
            if verbose:
                result[tx_hash] = raw_transactions_cache[tx_hash]
                result[tx_hash] = raM
w_transactions_cache[tx_hash]['hex'] if raw_transactions_cache[tx_hash] is not None else None
        except KeyError as e: #shows up most likely due to finickyness with addrindex not always returning results that we need...
            _logger.warning("tx missing in rawtx cache: {} -- txhash_list size: {}, hash: {} / raw_transactions_cache size: {} / # rpc_batch calls: {} / txhash in noncached_txhashes: {} / txhash in txhash_list: {} -- list {}".format(
                e, len(txhash_list), hashlib.md5(json.dumps(lM
ist(txhash_list)).encode()).hexdigest(), len(raw_transactions_cache), len(payload),
                tx_hash in noncached_txhashes, tx_hash in txhash_list, list(txhash_list.difference(noncached_txhashes)) ))
            if  _retry < GETRAWTRANSACTION_MAX_RETRIES: #try again
                time.sleep(0.05 * (_retry + 1)) # Wait a bit, hitting the index non-stop may cause it to just break down... TODO: Better handling
                r = getrawtransaction_batch([tx_hash], verbose=verbose, skip_missing=skip_missing, _M
                result[tx_hash] = r[tx_hash]
                raise #already tried again, give up
def get_unspent_txouts(source):
    return indexd_rpc_call('/a/'+source+'/utxos')
def search_raw_transactions(address, unconfirmed=True):
    all_transactions = indexd_rpc_call('/a/'+address+'/txs?verbose=1')
        return all_transactions
    # filter for confirmed transactions only
    confirmed_transactions = list(filter(lambda t: 'confirmaM
tions' in t and t['confirmations'] > 0, all_transactions))
    return confirmed_transactions
def getindexblocksbehind():
    status = indexd_rpc_call('/status')
    if status['ready']:
    if status['blocksBehind']:
        return status['blocksBehind']
    raise IndexdRPCError('Unknown status for indexd')
def indexd_rpc_call(path):
    url = config.INDEXD_URL+path
        response = requests.get(url, headers={'content-type': 'application/json'},
t=config.REQUESTS_TIMEOUT)
    except (Timeout, ReadTimeout, ConnectionError):
        logger.debug('Could not connect to backend at `{}`.'.format(util.clean_url_for_log(url),))
    if response == None:
        if config.TESTNET:
            network = 'testnet'
        elif config.REGTEST:
            network = 'regtest'
            network = 'mainnet'
        raise IndexdRPCError('Cannot communicate with {} indexd server at `{}`.'.format(network, util.clean_url_for_log(url)))
    elif response.statuM
        raise IndexdRPCError('Indexd returned error: {} {} {}'.format(response.status_code, response.reason, response.text))
    elif response.status_code not in (200, 500):
        raise IndexdRPCError("Bad response from {}: {} {}".format(util.clean_url_for_log(url), response.status_code, response.reason))
    # Return result, with error handling.
    response_json = response.json()
    if isinstance(response_json, (list, tuple)) or 'error' not in response_json.keys() or response_json['error'] == NM
        return response_json
        raise IndexdRPCError('{}'.format(response_json['error']))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
logger = logging.getLogger(__name__)
from decimal import Decimal as D
import bitcoin as bitcoinlib
import bitcoin.rpc as bitcoinlib_rpc
from bitcoin.core import CBlock
from counterpartylib.lib import util
from counterpartylib.lib import script
from counterpartylib.lib import config
from counterpartylib.lib import exceptions
from counterpartylib.lib.backend import addrindexrs
MEMPOOL_CACHE_INITIALIZED = False
def sortkeypicker(keynames):
    """http://stackoverflow.com/a/1143719"""
    for i, k in enumerate(keynames):
        if k[:1] == '-':
            keynames[i] = k[1:]
            negate.add(k[1:])
    def getit(adict):
       composite = [adict[k] for k in keynames]
       for i, (k, v) in enumerate(zip(keynamM
           if k in negate:
               composite[i] = -v
       return composite
    mdl = sys.modules['counterpartylib.lib.backend.{}'.format(config.BACKEND_NAME)]
    BACKEND().stop()
def getblockcount():
    return BACKEND().getblockcount()
def getblockhash(blockcount):
    return BACKEND().getblockhash(blockcount)
def getblock(block_hash):
    block_hex = BACKEND().getblock(block_hash)
    return CBlock.deserializM
e(util.unhexlify(block_hex))
def cache_pretx(txid, rawtx):
    PRETX_CACHE[binascii.hexlify(txid).decode('utf8')] = binascii.hexlify(rawtx).decode('utf8')
def clear_pretx(txid):
    del PRETX_CACHE[binascii.hexlify(txid).decode('utf8')]
def getrawtransaction(tx_hash, verbose=False, skip_missing=False):
    if tx_hash in PRETX_CACHE:
        return PRETX_CACHE[tx_hash]
        return BACKEND().getrawtransaction(tx_hash, verbose=verbose, skip_missing=skip_missing)
def getrawtransaction_batch(txhash_listM
, verbose=False, skip_missing=False):
    return BACKEND().getrawtransaction_batch(txhash_list, verbose=verbose, skip_missing=skip_missing)
def sendrawtransaction(tx_hex):
    return BACKEND().sendrawtransaction(tx_hex)
def getrawmempool():
    return BACKEND().getrawmempool()
def getindexblocksbehind():
    return BACKEND().getindexblocksbehind()
def extract_addresses(txhash_list):
    return BACKEND().extract_addresses(txhash_list)
def ensure_script_pub_key_for_inputs(coins):
    txhash_set = set()
        if 'scriptPubKey' not in coin:
            txhash_set.add(coin['txid'])
    if len(txhash_set) > 0:
        txs = BACKEND().getrawtransaction_batch(list(txhash_set), verbose=True, skip_missing=False)
        for coin in coins:
            if 'scriptPubKey' not in coin:
                # get the scriptPubKey
                txid = coin['txid']
                for vout in txs[txid]['vout']:
                    if vout['n'] == coin['vout']:
                        coin['scriptPubKey'] = vout['scM
def fee_per_kb(conf_target, mode, nblocks=None):
    :param conf_target:
    :return: fee_per_kb in satoshis, or None when unable to determine
    return BACKEND().fee_per_kb(conf_target, mode, nblocks=nblocks)
def deserialize(tx_hex):
    return bitcoinlib.core.CTransaction.deserialize(binascii.unhexlify(tx_hex))
    return bitcoinlib.core.CTransaction.serialize(ctx)
def is_valid(address):
        script.valiM
    except script.AddressError:
        return False
def get_txhash_list(block):
    return [bitcoinlib.core.b2lx(ctx.GetHash()) for ctx in block.vtx]
def get_tx_list(block):
    raw_transactions = {}
    tx_hash_list = []
    for ctx in block.vtx:
        if util.enabled('correct_segwit_txids'):
            hsh = ctx.GetTxid()
            hsh = ctx.GetHash()
        tx_hash = bitcoinlib.core.b2lx(hsh)
        raw = ctx.serialize()
        tx_hash_list.append(tx_hM
        raw_transactions[tx_hash] = bitcoinlib.core.b2x(raw)
    return (tx_hash_list, raw_transactions)
def sort_unspent_txouts(unspent, unconfirmed=False):
    # Filter out all dust amounts to avoid bloating the resultant transaction
    unspent = list(filter(lambda x: x['value'] > config.DEFAULT_MULTISIG_DUST_SIZE, unspent))
    # Sort by amount, using the largest UTXOs available
    if config.REGTEST:
        # REGTEST has a lot of coinbase inputs that can't be spent due to maturity
        # this doesn'M
t usually happens on mainnet or testnet because most fednodes aren't mining
        unspent = sorted(unspent, key=lambda x: (x['confirmations'], x['value']), reverse=True)
        unspent = sorted(unspent, key=lambda x: x['value'], reverse=True)
def get_btc_supply(normalize=False):
    """returns the total supply of {} (based on what Bitcoin Core says the current block height is)""".format(config.BTC)
    block_count = getblockcount()
    blocks_remaining = block_count
    while blocks_remaining > 0:
        if blocks_remaining >= 210000:
            blocks_remaining -= 210000
            total_supply += 210000 * reward
            reward /= 2
            total_supply += (blocks_remaining * reward)
            blocks_remaining = 0
    return total_supply if normalize else int(total_supply * config.UNIT)
class MempoolError(Exception):
def get_unspent_txouts(source, unconfirmed=False, unspent_tx_hash=None):
    """returns a list of uM
nspent outputs for a specific address
    @return: A list of dicts, with each entry in the dict having the following keys:
    unspent = BACKEND().get_unspent_txouts(source)
    # filter by unspent_tx_hash
    if unspent_tx_hash is not None:
        unspent = list(filter(lambda x: x['txId'] == unspent_tx_hash, unspent))
    # filter unconfirmed
    if not unconfirmed:
        unspent = [utxo for utxo in unspent if utxo['confirmations'] > 0]
    for utxo in unspent:
        utxo['amount'] = M
float(utxo['value'] / config.UNIT)
        utxo['txid'] = utxo['txId']
        del utxo['txId']
        # do not add scriptPubKey
def search_raw_transactions(address, unconfirmed=True):
    return BACKEND().search_raw_transactions(address, unconfirmed)
class UnknownPubKeyError(Exception):
def pubkeyhash_to_pubkey(pubkeyhash, provided_pubkeys=None):
    # Search provided pubkeys.
    if provided_pubkeys:
        if type(provided_pubkeys) != list:
            provided_pubkeys = [providM
        for pubkey in provided_pubkeys:
            if pubkeyhash == script.pubkey_to_pubkeyhash(util.unhexlify(pubkey)):
                return pubkey
            elif pubkeyhash == script.pubkey_to_p2whash(util.unhexlify(pubkey)):
                return pubkey
    # Search blockchain.
    raw_transactions = search_raw_transactions(pubkeyhash, unconfirmed=True)
    for tx_id in raw_transactions:
        tx = raw_transactions[tx_id]
        for vin in tx['vin']:
            if 'txinwitness' in vin:
             if len(vin['txinwitness']) >= 2:
                    # catch unhexlify errs for when txinwitness[1] isn't a witness program (eg; for P2W)
                    try:
                        pubkey = vin['txinwitness'][1]
                        if pubkeyhash == script.pubkey_to_p2whash(util.unhexlify(pubkey)):
                            return pubkey
                    except binascii.Error:
                        pass
            elif 'coinbase' not in vin:
                scriptsig = vin['scriptSig']M
                asm = scriptsig['asm'].split(' ')
                if len(asm) >= 2:
                    # catch unhexlify errs for when asm[1] isn't a pubkey (eg; for P2SH)
                    try:
                        pubkey = asm[1]
                        if pubkeyhash == script.pubkey_to_pubkeyhash(util.unhexlify(pubkey)):
                            return pubkey
                    except binascii.Error:
                        pass
    raise UnknownPubKeyError('Public key was neither provided nor publisM
hed in blockchain.')
def multisig_pubkeyhashes_to_pubkeys(address, provided_pubkeys=None):
    signatures_required, pubkeyhashes, signatures_possible = script.extract_array(address)
    pubkeys = [pubkeyhash_to_pubkey(pubkeyhash, provided_pubkeys) for pubkeyhash in pubkeyhashes]
    return script.construct_array(signatures_required, pubkeys, signatures_possible)
def init_mempool_cache():
    """prime the mempool cache, so that functioning is faster...
    global MEMPOOL_CACHE_INITIALIZED
g('Initializing mempool cache...')
    start = time.time()
    mempool_txhash_list = getrawmempool()
    #with this function, don't try to load in more than BACKEND_RAW_TRANSACTIONS_CACHE_SIZE entries
    num_tx = min(len(mempool_txhash_list), config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE)
    mempool_tx = BACKEND().getrawtransaction_batch(mempool_txhash_list[:num_tx], skip_missing=True, verbose=True)
    vin_txhash_list = []
    max_remaining_num_tx = config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE - num_tx
        for txid in mempool_tx:
            tx = mempool_tx[txid]
            if not(tx is None):
                vin_txhash_list += [vin['txid'] for vin in tx['vin']]
        BACKEND().getrawtransaction_batch(vin_txhash_list[:max_remaining_num_tx], skip_missing=True, verbose=True)
    MEMPOOL_CACHE_INITIALIZED = True
    logger.info('Mempool cache initialized: {:.2f}s for {:,} transactions'.format(time.time() - start, num_tx + min(max_remaining_num_tx, len(vin_txhash_list))))
expandtab shiftwidth=4 softtabstop=4
#### bc_data_stream.py
# Workalike python implementation of Bitcoin's CDataStream class.
from .exceptions import SerializationError
class BCDataStream(object):
  def __init__(self):
    self.input = None
    self.read_cursor = 0
    self.input = None
    self.read_cursor = 0
  def write(self, bytes):  # Initialize with string of bytes
    if self.input is None:
      self.input = bytes
      self.input += bM
  def map_file(self, file, start):  # Initialize with bytes from file
    self.input = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ)
    self.read_cursor = start
  def seek_file(self, position):
    self.read_cursor = position
  def close_file(self):
    self.input.close()
  def read_string(self):
    # Strings are encoded depending on length:
    # 0 to 252 :  1-byte-length followed by bytes (if any)
    # 253 to 65,535 : byte'253' 2-byte-length followed by bytes
    # 65,536 to 4,294,967,295 : bytM
e '254' 4-byte-length followed by bytes
    # ... and the Bitcoin client is coded to understand:
    # greater than 4,294,967,295 : byte '255' 8-byte-length followed by bytes of string
    # ... but I don't think it actually handles any strings that big.
    if self.input is None:
      raise SerializationError("call write(bytes) before trying to deserialize")
      length = self.read_compact_size()
    except IndexError:
      raise SerializationError("attempt to read past end of buffer")
f.read_bytes(length)
  def write_string(self, string):
    # Length-encoded as with read-string
    self.write_compact_size(len(string))
    self.write(string)
  def read_bytes(self, length):
      result = self.input[self.read_cursor:self.read_cursor+length]
      self.read_cursor += length
    except IndexError:
      raise SerializationError("attempt to read past end of buffer")
  def read_boolean(self): return self.read_bytes(1)[0] != chr(0)
  def read_int16(self):M
 return self._read_num('<h')
  def read_uint16(self): return self._read_num('<H')
  def read_int32(self): return self._read_num('<i')
  def read_uint32(self): return self._read_num('<I')
  def read_int64(self): return self._read_num('<q')
  def read_uint64(self): return self._read_num('<Q')
  def write_boolean(self, val): return self.write(chr(1) if val else chr(0))
  def write_int16(self, val): return self._write_num('<h', val)
  def write_uint16(self, val): return self._write_num('<H', val)
  def write_int32(selM
f, val): return self._write_num('<i', val)
  def write_uint32(self, val): return self._write_num('<I', val)
  def write_int64(self, val): return self._write_num('<q', val)
  def write_uint64(self, val): return self._write_num('<Q', val)
  def read_compact_size(self):
    size = self.input[self.read_cursor]
    self.read_cursor += 1
      size = self._read_num('<H')
    elif size == 254:
      size = self._read_num('<I')
    elif size == 255:
      size = self._read_num('<Q')
def write_compact_size(self, size):
      raise SerializationError("attempt to write size < 0")
    elif size < 253:
       self.write(chr(size))
    elif size < 2**16:
      self.write('\xfd')
      self._write_num('<H', size)
    elif size < 2**32:
      self.write('\xfe')
      self._write_num('<I', size)
    elif size < 2**64:
      self.write('\xff')
      self._write_num('<Q', size)
  def _read_num(self, format):
    (i,) = struct.unpack_from(format, self.input, self.read_cursor)
ad_cursor += struct.calcsize(format)
  def _write_num(self, format, num):
    s = struct.pack(format, num)
  def read_var_int(self):
        cur_byte = self.read_bytes(1)[0]
        n = (n << 7) | (cur_byte & 0x7F)
        if cur_byte & 0x80:
            return n
#### blocks_parser.py
import os, json, time, logging, binascii
logger = logging.getLogger(__name__)
from .bc_data_stream import BCDataStrM
from .utils import b2h, double_hash, ib2h, inverse_hash
def open_leveldb(db_dir):
        import plyvel
        raise Exception("Please install the plyvel package via pip3.")
        return plyvel.DB(db_dir, create_if_missing=False)
    except plyvel._plyvel.IOError as e:
        logger.info(str(e))
        raise Exception("Ensure that bitcoind is stopped.")
class BlockchainParser():
    def __init__(self, blocks_dir, leveldb_dir):
        self.blocks_dir = blocks_dir
self.leveldb_dir = leveldb_dir
        self.file_num = -1
        self.current_file_size = 0
        self.current_block_file = None
        self.data_stream = None
        self.ldb = open_leveldb(self.leveldb_dir)
    def read_tx_in(self, vds):
        tx_in['txid'] = ib2h(vds.read_bytes(32))
        tx_in['vout'] = vds.read_uint32()
        script_sig_size = vds.read_compact_size()
        tx_in['scriptSig'] = b2h(vds.read_bytes(script_sig_size))
        tx_in['sequence'] = vds.read_uint32()
      if tx_in['txid'] == '0000000000000000000000000000000000000000000000000000000000000000':
            tx_in = {
                'coinbase': tx_in['scriptSig'],
                'sequence': tx_in['sequence']
        return tx_in
    def read_tx_out(self, vds):
        tx_out['value'] = vds.read_int64() / 100000000
        script = vds.read_bytes(vds.read_compact_size())
        tx_out['scriptPubKey'] = {
            'hex': b2h(script)
        return tx_out
d_transaction(self, vds):
        transaction = {}
        start_pos = vds.read_cursor
        transaction['version'] = vds.read_int32()
        transaction['vin'] = []
        for i in range(vds.read_compact_size()):
            transaction['vin'].append(self.read_tx_in(vds))
        transaction['vout'] = []
        for i in range(vds.read_compact_size()):
            transaction['vout'].append(self.read_tx_out(vds))
        transaction['lock_time'] = vds.read_uint32()
        data = vds.input[start_pos:vds.reaM
        transaction['tx_hash'] = ib2h(double_hash(data))
        transaction['__data__'] = b2h(data)
        return transaction
    def read_block_header(self, vds):
        block_header = {}
        block_header['magic_bytes'] = vds.read_int32()
        #if block_header['magic_bytes'] != 118034699:
         #   raise Exception('Not a block')
        block_header['block_size'] = vds.read_int32()
        header_start = vds.read_cursor
        block_header['version'] = vds.read_int32()
        block_headerM
['hash_prev'] = ib2h(vds.read_bytes(32))
        block_header['hash_merkle_root'] = ib2h(vds.read_bytes(32))
        block_header['block_time'] = vds.read_uint32()
        block_header['bits'] = vds.read_uint32()
        block_header['nonce'] = vds.read_uint32()
        header_end = vds.read_cursor
        header = vds.input[header_start:header_end]
        block_header['block_hash'] = ib2h(double_hash(header))
        block_header['__header__'] = b2h(header)
        return block_header
    def read_block(self, vdM
        block = self.read_block_header(vds)
        block['transaction_count'] = vds.read_compact_size()
        block['transactions'] = []
        for i in range(block['transaction_count']):
            block['transactions'].append(self.read_transaction(vds))
        return block
    def prepare_data_stream(self, file_num, pos_in_file):
        if self.data_stream is None or file_num != self.file_num:
            self.file_num = file_num
            if self.current_block_file:
                self.current_bloM
            data_file_path = os.path.join(self.blocks_dir, 'blk%05d.dat' % (self.file_num,))
            self.current_block_file = open(data_file_path, "rb")
            self.data_stream = BCDataStream()
            self.data_stream.map_file(self.current_block_file, pos_in_file)
            self.data_stream.seek_file(pos_in_file)
    def read_raw_block(self, block_hash):
        block_hash = binascii.unhexlify(inverse_hash(block_hash))
        block_data = self.ldb.get(bytes('b', 'utfM
        ds = BCDataStream()
        ds.write(block_data)
        version = ds.read_var_int()
        height = ds.read_var_int()
        status = ds.read_var_int()
        tx_count = ds.read_var_int()
        file_num = ds.read_var_int()
        block_pos_in_file = ds.read_var_int() - 8
        block_undo_pos_in_file = ds.read_var_int()
        block_header = ds.read_bytes(80)
        self.prepare_data_stream(file_num, block_pos_in_file)
        block = self.read_block(self.data_stream)
 block['block_index'] = height
        return block
    def read_raw_transaction(self, tx_hash):
        tx_hash = binascii.unhexlify(inverse_hash(tx_hash))
        tx_data = self.ldb.get(bytes('t', 'utf-8') + tx_hash)
        ds = BCDataStream()
        ds.write(tx_data)
        file_num = ds.read_var_int()
        block_pos_in_file = ds.read_var_int()
        tx_pos_in_block = ds.read_var_int()
        tx_pos_in_file = block_pos_in_file + 80 + tx_pos_in_block
        self.prepare_data_stream(file_nuM
        transaction = self.read_transaction(self.data_stream)
        return transaction
    def close(self):
        if self.current_block_file:
            self.current_block_file.close()
        self.ldb.close()
class ChainstateParser():
    def __init__(self, leveldb_dir):
        self.ldb = open_leveldb(leveldb_dir)
    def get_last_block_hash(self):
        block_hash = self.ldb.get(bytes('B', 'utf-8'))
        block_hash = ib2h(block_hash)
        return block_hash
        self.ldb.close()
class SolvingError(Exception): pass
class SerializationError(Exception):
  """ Thrown when there's a problem deserializing or serializing """
import binascii, os, random, json, hashlib
bytes_from_int = chr if bytes == str else lambda x: bytes([x])
    return binascii.hexlify(b).decode('utf-8')
def random_hex(length):
    return binascii.b2a_hex(os.urandom(length))
def double_hash(b):
	return hashlib.sha256(hashliM
b.sha256(b).digest()).digest()
def inverse_hash(hashstring):
	hashstring = hashstring[::-1]
	return ''.join([hashstring[i:i+2][::-1] for i in range(0, len(hashstring), 2)])
	return inverse_hash(b2h(b))
class JsonDecimalEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o,  decimal.Decimal):
            return str(o)
        return super(DecimalEncoder, self).default(o)
#### p2sh_encoding.py
This module contains p2sh data encodingM
import traceback # not needed if not printing exceptions on p2sh decoding
logger = logging.getLogger(__name__)
import bitcoin as bitcoinlib
from bitcoin.core.script import CScript
from counterpartylib.lib import config
from counterpartylib.lib import script
from counterpartylib.lib import exceptions
def maximum_data_chunk_size(pubkeylength):
    if pubkeylength >= 0:
        return bitcoinlib.core.script.MAX_SCRIPT_ELEMENT_SIZE - len(confiM
g.PREFIX) - pubkeylength - 12 #Two bytes are for unique offset. This will work for a little more than 1000 outputs
        return bitcoinlib.core.script.MAX_SCRIPT_ELEMENT_SIZE - len(config.PREFIX) - 44 # Redeemscript size for p2pkh addresses, multisig won't work here
def calculate_outputs(destination_outputs, data_array, fee_per_kb, exact_fee=None):
    datatx_size = 10  # 10 base
    datatx_size += 181  # 181 for source input
    datatx_size += (25 + 9) * len(destination_outputs)  # destination outputsM
    datatx_size += 13  # opreturn that signals P2SH encoding
    datatx_size += len(data_array) * (9 + 181)  # size of p2sh inputs, excl data
    datatx_size += sum([len(data_chunk) for data_chunk in data_array])  # data in scriptSig
    datatx_necessary_fee = int(datatx_size / 1000 * fee_per_kb)
    pretx_output_size = 10  # 10 base
    pretx_output_size += len(data_array) * 29  # size of P2SH output
    size_for_fee = pretx_output_size
    # split the tx fee evenly between all datatx outputs
= math.ceil(datatx_necessary_fee / len(data_array))
    data_value = config.DEFAULT_REGULAR_DUST_SIZE
    # adjust the data output with the new value and recalculate data_btc_out
    data_btc_out = data_value * len(data_array)
        remain_fee = exact_fee - data_value * len(data_array)
        if remain_fee > 0:
            #if the dust isn't enough to reach the exact_fee, data value will be an array with only the last fee bumped
            data_value = [data_value for i in range(len(data_arrM
            data_value[len(data_array)-1] = data_value[len(data_array)-1] + remain_fee
            data_btc_out = exact_fee
    data_output = (data_array, data_value)
    logger.getChild('p2shdebug').debug('datatx size: %d fee: %d' % (datatx_size, datatx_necessary_fee))
    logger.getChild('p2shdebug').debug('pretx output size: %d' % (pretx_output_size, ))
    logger.getChild('p2shdebug').debug('size_for_fee: %d' % (size_for_fee, ))
    return size_for_fee, datatx_necessary_fee, data_value, dM
def decode_p2sh_input(asm, p2sh_is_segwit=False):
    ''' Looks at the scriptSig for the input of the p2sh-encoded data transaction
        [signature] [data] [OP_HASH160 ... OP_EQUAL]
    pubkey, source, redeem_script_is_valid, found_data = decode_data_redeem_script(asm[-1], p2sh_is_segwit)
    if redeem_script_is_valid:
        # this is a signed transaction, so we got {sig[,sig]} {datachunk} {redeemScript}
        datachunk = found_data
        redeemScript = asm[-1] #asm[-2:]
    #print('ASM:', len(asm))
        pubkey, source, redeem_script_is_valid, found_data = decode_data_redeem_script(asm[-1], p2sh_is_segwit)
        if not redeem_script_is_valid or len(asm) != 3:
            return None, None, None
        # this is an unsigned transaction (last is outputScript), so we got [datachunk] [redeemScript] [temporaryOutputScript]
        datachunk, redeemScript, _substituteScript = asm
    data = datachunk
    if data[:len(config.PREFIX)] == config.PREFIX:
        data = data[len(confiM
        if data == b'':
            return source, None, None
        raise exceptions.DecodeError('unrecognised P2SH output')
    return source, None, data
def decode_data_push(arr, pos):
    opcode = bitcoinlib.core.script.CScriptOp(arr[pos])
    if opcode > 0 and opcode < bitcoinlib.core.script.OP_PUSHDATA1:
        pushlen = arr[pos]
    elif opcode == bitcoinlib.core.script.OP_PUSHDATA1:
        pushlen = arr[pos + 1]
elif opcode == bitcoinlib.core.script.OP_PUSHDATA2:
        (pushlen, ) = struct.unpack('<H', arr[pos + 1:pos + 3])
    elif opcode == bitcoinlib.core.script.OP_PUSHDATA4:
        (pushlen, ) = struct.unpack('<L', arr[pos + 1:pos + 5])
    return pos + pushlen, arr[pos:pos + pushlen]
def decode_data_redeem_script(redeemScript, p2sh_is_segwit=False):
    script_len = len(redeemScript)
    found_data = b''
    if script_len == 41 and \
        redeemScript[0] == bitcoinlib.core.scM
        redeemScript[35] == bitcoinlib.core.script.OP_CHECKSIGVERIFY and \
        redeemScript[37] == bitcoinlib.core.script.OP_DROP and \
        redeemScript[38] == bitcoinlib.core.script.OP_DEPTH and \
        redeemScript[39] == bitcoinlib.core.script.OP_0 and \
        redeemScript[40] == bitcoinlib.core.script.OP_EQUAL:
            # - OP_DROP [push] [33-byte pubkey] OP_CHECKSIGVERIFY [n] OP_DROP OP_DEPTH 0 OP_EQUAL
            pubkey = redeemScript[2:35]
            if p2sh_is_segwit:
             source = script.pubkey_to_p2whash(pubkey)
                source = script.pubkey_to_pubkeyhash(pubkey)
            redeem_script_is_valid = True
    elif script_len > 41 and \
        redeemScript[0] == bitcoinlib.core.script.OP_DROP and \
        redeemScript[script_len-4] == bitcoinlib.core.script.OP_DROP and \
        redeemScript[script_len-3] == bitcoinlib.core.script.OP_DEPTH and \
        redeemScript[script_len-2] == bitcoinlib.core.script.OP_0 and \
        redeemScript[scripM
t_len-1] == bitcoinlib.core.script.OP_EQUAL:
            # - OP_DROP {arbitrary multisig script} [n] OP_DROP OP_DEPTH 0 OP_EQUAL
            pubkey = None
            source = None
            redeem_script_is_valid = True
        pubkey = None
        source = None
        redeem_script_is_valid = False
            opcode = bitcoinlib.core.script.CScriptOp(redeemScript[0])
            if opcode > bitcoinlib.core.script.OP_0 and opcode < bitcoinlib.core.script.OP_PUSHDATA1 or \
   opcode in (bitcoinlib.core.script.OP_PUSHDATA1, bitcoinlib.core.script.OP_PUSHDATA2, bitcoinlib.core.script.OP_PUSHDATA4):
                pos = 0
                pos, found_data = decode_data_push(redeemScript, 0)
                if redeemScript[pos] == bitcoinlib.core.script.OP_DROP:
                    pos += 1
                    valid_sig = False
                    opcode = redeemScript[pos]
                    if type(opcode) != type(''):
                        if opcode >= bitcoinlib.core.script.OP_2 M
and opcode <= bitcoinlib.core.script.OP_15:
                            # it's multisig
                            req_sigs = opcode - bitcoinlib.core.script.OP_1 + 1
                            pos += 1
                            pubkey = None
                            num_sigs = 0
                            found_sigs = False
                            while not found_sigs:
                                pos, npubkey = decode_data_push(redeemScript, pos)
                                num_sigs += 1
                          if redeemScript[pos] - bitcoinlib.core.script.OP_1 + 1 == num_sigs:
                                    found_sigs = True
                            pos += 1
                            valid_sig = redeemScript[pos] == bitcoinlib.core.script.OP_CHECKMULTISIGVERIFY
                        else:
                            # it's p2pkh
                            pos, pubkey = decode_data_push(redeemScript, pos)
                            if p2sh_is_segwit:
                              M
  source = script.pubkey_to_p2whash(pubkey)
                            else:
                                source = script.pubkey_to_pubkeyhash(pubkey)
                            valid_sig = redeemScript[pos] == bitcoinlib.core.script.OP_CHECKSIGVERIFY
                        pos += 1
                        if valid_sig:
                            uniqueOffsetLength = 0
                            for i in range(pos+1, len(redeemScript)):
                                if redeemScript[i] == bitcoinlib.corM
                                    uniqueOffsetLength = i-pos-1
                                    break
                            redeem_script_is_valid = redeemScript[pos + 1 + uniqueOffsetLength] == bitcoinlib.core.script.OP_DROP and \
                                redeemScript[pos + 2 + uniqueOffsetLength] == bitcoinlib.core.script.OP_DEPTH and \
                                redeemScript[pos + 3 + uniqueOffsetLength] == 0 and \
                                redeemScript[pos + 4 + uM
niqueOffsetLength] == bitcoinlib.core.script.OP_EQUAL
        except Exception as e:
            pass #traceback.print_exc()
    return pubkey, source, redeem_script_is_valid, found_data
def make_p2sh_encoding_redeemscript(datachunk, n, pubKey=None, multisig_pubkeys=None, multisig_pubkeys_required=None):
    _logger = logger.getChild('p2sh_encoding')
    assert len(datachunk) <= bitcoinlib.core.script.MAX_SCRIPT_ELEMENT_SIZE
    dataDropScript = [datachunk, bitcoinlib.core.script.OP_DROP] # just drop the data chM
    cleanupScript = [n, bitcoinlib.core.script.OP_DROP, bitcoinlib.core.script.OP_DEPTH, 0, bitcoinlib.core.script.OP_EQUAL] # unique offset + prevent scriptSig malleability
    if pubKey is not None:
        # a p2pkh script looks like this: {pubkey} OP_CHECKSIGVERIFY
        verifyOwnerScript = [pubKey, bitcoinlib.core.script.OP_CHECKSIGVERIFY]
    elif multisig_pubkeys_required is not None and multisig_pubkeys:
        # a 2-of-3 multisig looks like this:
        #   2 {pubkey1} {pubkey2} {pubkey3} 3 OP_CHEM
        multisig_pubkeys_required = int(multisig_pubkeys_required)
        if multisig_pubkeys_required < 2 or multisig_pubkeys_required > 15:
            raise exceptions.TransactionError('invalid multisig pubkeys value')
        verifyOwnerScript = [multisig_pubkeys_required]
        for multisig_pubkey in multisig_pubkeys:
            verifyOwnerScript.append(multisig_pubkey)
        verifyOwnerScript = verifyOwnerScript + [len(multisig_pubkeys), bitcoinlib.core.script.OP_CHECKMULTISIGVERIFY]
        raise exceptions.TransactionError('Either pubKey or multisig pubKeys must be provided')
    #redeemScript = CScript(datachunk) + CScript(dataDropScript + verifyOwnerScript + cleanupScript)
    redeemScript = CScript(dataDropScript + verifyOwnerScript + cleanupScript)
    _logger.debug('datachunk %s' % (binascii.hexlify(datachunk)))
    _logger.debug('dataDropScript %s (%s)' % (repr(CScript(dataDropScript)), binascii.hexlify(CScript(dataDropScript))))
    _logger.debug('verifyOwnerScript %s (%s)' %M
 (repr(CScript(verifyOwnerScript)), binascii.hexlify(CScript(verifyOwnerScript))))
    _logger.debug('entire redeemScript %s (%s)' % (repr(redeemScript), binascii.hexlify(redeemScript)))
    #scriptSig = CScript([]) + redeemScript  # PUSH(datachunk) + redeemScript
    scriptSig = CScript([redeemScript])
    outputScript = redeemScript.to_p2sh_scriptPubKey()
    _logger.debug('scriptSig %s (%s)' % (repr(scriptSig), binascii.hexlify(scriptSig)))
    _logger.debug('outputScript %s (%s)' % (repr(outputScript), binascM
ii.hexlify(outputScript)))
    # outputScript looks like OP_HASH160 {{ hash160([redeemScript]) }} OP_EQUALVERIFY
    # redeemScript looks like OP_DROP {{ pubkey }} OP_CHECKSIGVERIFY {{ n }} OP_DROP OP_DEPTH 0 OP_EQUAL
    # scriptSig is {{ datachunk }} OP_DROP {{ pubkey }} OP_CHECKSIGVERIFY {{ n }} OP_DROP OP_DEPTH 0 OP_EQUAL
    return scriptSig, redeemScript, outputScript
def make_standard_p2sh_multisig_script(multisig_pubkeys, multisig_pubkeys_required):
    # a 2-of-3 multisig looks like this:
ey1} {pubkey2} {pubkey3} 3 OP_CHECKMULTISIG
    multisig_pubkeys_required = int(multisig_pubkeys_required)
    multisig_script = [multisig_pubkeys_required]
    for multisig_pubkey in multisig_pubkeys:
        multisig_script.append(multisig_pubkey)
    multisig_script = multisig_script + [len(multisig_pubkeys), bitcoinlib.core.script.OP_CHECKMULTISIG]
    return multisig_script
Construct and serialize the Bitcoin transactions that are Counterparty transactions.
logger = logging.getLogger(__name__)
import bitcoin as bitcoinlib
from bitcoin.core import Hash160
from bitcoin.core.script import CScript
from bitcoin.wallet import P2PKHBitcoinAddress, P2SHBitcoinAddress
from counterpartylib.lib import config
from counterpartylib.lib import exceptions
from counterpartylib.lib import util
ounterpartylib.lib import script
from counterpartylib.lib import backend
from counterpartylib.lib import arc4
from counterpartylib.lib.transaction_helper import p2sh_encoding
from bitcoin.bech32 import CBech32Data
OP_PUSHDATA1 = b'\x4c'
OP_HASH160 = b'\xa9'
OP_EQUALVERIFY = b'\x88'
OP_CHECKSIG = b'\xac'
OP_CHECKMULTISIG = b'\xae'
UTXO_LOCKS_PER_ADDREM
SS_MAXSIZE = 5000  # set higher than the max number of UTXOs we should expect to
                                       # manage in an aging cache for any one source address, at any one period
        return (i).to_bytes(1, byteorder='little')
    elif i <= 0xffff:
        return b'\xfd' + (i).to_bytes(2, byteorder='little')
    elif i <= 0xffffffff:
        return b'\xfe' + (i).to_bytes(4, byteorder='little')
        return b'\xff' + (i).to_bytes(8, byteorder='little')
        return (i).to_bytes(1, byteorder='little')              # Push i bytes.
        return b'\x4c' + (i).to_bytes(1, byteorder='little')    # OP_PUSHDATA1
    elif i <= 0xffff:
        return b'\x4d' + (i).to_bytes(2, byteorder='little')    # OP_PUSHDATA2
        return b'\x4e' + (i).to_bytes(4, byteorder='little')    # OP_PUSHDATA4
def get_script(address):
    if script.is_multisig(address):
        return get_multisig_script(address)
 script.is_bech32(address):
        return get_p2w_script(address)
            return get_monosig_script(address)
        except script.VersionByteError as e:
            return get_p2sh_script(address)
def get_multisig_script(address):
    signatures_required, pubkeys, signatures_possible = script.extract_array(address)
    # Required signatures.
    if signatures_required == 1:
        op_required = OP_1
    elif signatures_required == 2:
    elif signatures_required == 3:
        op_required = OP_3
        raise script.InputError('Required signatures must be 1, 2 or 3.')
    # Required signatures.
    # Note 1-of-1 addresses are not supported (they don't go through extract_array anyway).
    if signatures_possible == 2:
        op_total = OP_2
    elif signatures_possible == 3:
        op_total = OP_3
        raise script.InputError('Total possible signatures must be 2 or 3.')
    # Construct script.
 = op_required                                # Required signatures
    for public_key in pubkeys:
        public_key = binascii.unhexlify(public_key)
        tx_script += op_push(len(public_key))              # Push bytes of public key
        tx_script += public_key                            # Data chunk (fake) public key
    tx_script += op_total                                  # Total signatures
    tx_script += OP_CHECKMULTISIG                          # OP_CHECKMULTISIG
    return (tx_script, None)
et_monosig_script(address):
    # Construct script.
    pubkeyhash = script.base58_check_decode(address, config.ADDRESSVERSION)
    tx_script = OP_DUP                                     # OP_DUP
    tx_script += OP_HASH160                                # OP_HASH160
    tx_script += op_push(20)                               # Push 0x14 bytes
    tx_script += pubkeyhash                                # pubKeyHash
    tx_script += OP_EQUALVERIFY                            # OP_EQUALVERIFY
    tx_script += OP_CHECKSM
IG                               # OP_CHECKSIG
    return (tx_script, None)
def get_p2sh_script(address):
    # Construct script.
    scripthash = script.base58_check_decode(address, config.P2SH_ADDRESSVERSION)
    tx_script = OP_HASH160
    tx_script += op_push(len(scripthash))
    tx_script += scripthash
    tx_script += OP_EQUAL
    return (tx_script, None)
def get_p2w_script(address):
    # Construct script.
    scripthash = bytes(CBech32Data(address))
    if len(scripthash) == 20:
        # P2WPKH encM
        tx_script = OP_0
        tx_script += b'\x14'
        tx_script += scripthash
        witness_script = OP_HASH160
        witness_script += op_push(len(scripthash))
        witness_script += scripthash
        witness_script += OP_EQUAL
        return (tx_script, witness_script)
    elif len(scripthash) == 32:
        # P2WSH encoding
        raise Exception('P2WSH encoding not yet supported')
def make_fully_valid(pubkey_start):
    """Take a too short data pubkey and make it look like a real pubM
    Take an obfuscated chunk of data that is two bytes too short to be a pubkey and
    add a sign byte to its beginning and a nonce byte to its end. Choose these
    bytes so that the resulting sequence of bytes is a fully valid pubkey (i.e. on
    the ECDSA curve). Find the correct bytes by guessing randomly until the check
    passes. (In parsing, these two bytes are ignored.)
    assert type(pubkey_start) == bytes
    assert len(pubkey_start) == 31    # One sign byte and one nonce byte required (fM
    random_bytes = hashlib.sha256(pubkey_start).digest()      # Deterministically generated, for unit tests.
    sign = (random_bytes[0] & 0b1) + 2                  # 0x02 or 0x03
    nonce = initial_nonce = random_bytes[1]
    while not script.is_fully_valid(pubkey):
        # Increment nonce.
        assert nonce != initial_nonce
        # Construct a possibly fully valid public key.
        pubkey = bytes([sign]) + pubkey_start + bytes([nonce % 256])
rt len(pubkey) == 33
def serialise(encoding, inputs, destination_outputs, data_output=None, change_output=None, dust_return_pubkey=None):
    s  = (1).to_bytes(4, byteorder='little')                # Version
    use_segwit = False
    for i in range(len(inputs)):
        txin = inputs[i]
        spk = txin['scriptPubKey']
        if spk[0:2] == '00': # Witness version 0
            datalen = binascii.unhexlify(spk[2:4])[0]
            if datalen == 20 or datalen == 32:
                # 20 is fM
or P2WPKH and 32 is for P2WSH
                if not(use_segwit):
                    s  = (2).to_bytes(4, byteorder='little') # Rewrite version
                    use_segwit = True
                txin['is_segwit'] = True
        s += b'\x00' # marker
        s += b'\x01' # flag
    # Number of inputs.
    s += var_int(int(len(inputs)))
    witness_txins = []
    witness_data = {}
    # List of Inputs.
    for i in range(len(inputs)):
        txin = inputs[i]
        s += binascii.unhexlifM
y(bytes(txin['txid'], 'utf-8'))[::-1]         # TxOutHash
        s += txin['vout'].to_bytes(4, byteorder='little')   # TxOutIndex
        tx_script = binascii.unhexlify(bytes(txin['scriptPubKey'], 'utf-8'))
        s += var_int(int(len(tx_script)))                      # Script length
        s += tx_script                                         # Script
        s += b'\xff' * 4                                    # Sequence
    # Number of outputs.
    n += len(destination_outputs)
    if data_output:M
        data_array, value = data_output
        for data_chunk in data_array: n += 1
        data_array = []
    if change_output: n += 1
    # Destination output.
    for destination, value in destination_outputs:
        s += value.to_bytes(8, byteorder='little')          # Value
        tx_script, witness_script = get_script(destination)
        #if use_segwit and destination in witness_data: # Not deleteing, We will need this for P2WSH
        #    witness_data[destination].appeM
        #    tx_script = witness_script
        #if witness_script:
        #    tx_script = witness_script
        s += var_int(int(len(tx_script)))                      # Script length
        s += tx_script
    for data_chunk in data_array:
        data_array, value = data_output
        s += value.to_bytes(8, byteorder='little')        # Value
        data_chunk = config.PREFIX + data_chunk
        # Initialise encryption key (once per output).
        assert isinstancM
e(inputs[0]['txid'], str)
        key = arc4.init_arc4(binascii.unhexlify(inputs[0]['txid']))  # Arbitrary, easy
        if encoding == 'multisig':
            assert dust_return_pubkey
            # Get data (fake) public key.
            pad_length = (33 * 2) - 1 - 2 - 2 - len(data_chunk)
            assert pad_length >= 0
            data_chunk = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00')
            data_chunk = key.encrypt(data_chunk)
            data_pubkey_1 = maM
ke_fully_valid(data_chunk[:31])
            data_pubkey_2 = make_fully_valid(data_chunk[31:])
            # Construct script.
            tx_script = OP_1                                   # OP_1
            tx_script += op_push(33)                           # Push bytes of data chunk (fake) public key    (1/2)
            tx_script += data_pubkey_1                         # (Fake) public key                  (1/2)
            tx_script += op_push(33)                           # Push bytes of data chunk (fake) pubM
            tx_script += data_pubkey_2                         # (Fake) public key                  (2/2)
            tx_script += op_push(len(dust_return_pubkey))  # Push bytes of source public key
            tx_script += dust_return_pubkey                       # Source public key
            tx_script += OP_3                                  # OP_3
            tx_script += OP_CHECKMULTISIG                      # OP_CHECKMULTISIG
        elif encoding == 'opreturn':
            data_chunk = key.M
            tx_script = OP_RETURN                                  # OP_RETURN
            tx_script += op_push(len(data_chunk))                  # Push bytes of data chunk (NOTE: OP_SMALLDATA?)
            tx_script += data_chunk                                # Data
        elif encoding == 'pubkeyhash':
            pad_length = 20 - 1 - len(data_chunk)
            assert pad_length >= 0
            data_chunk = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00')
            data_chM
unk = key.encrypt(data_chunk)
            # Construct script.
            tx_script = OP_DUP                                     # OP_DUP
            tx_script += OP_HASH160                                # OP_HASH160
            tx_script += op_push(20)                               # Push 0x14 bytes
            tx_script += data_chunk                                # (Fake) pubKeyHash
            tx_script += OP_EQUALVERIFY                            # OP_EQUALVERIFY
            tx_script += OP_CHECKSIG          M
                     # OP_CHECKSIG
            raise exceptions.TransactionError('Unknown encoding
        s += var_int(int(len(tx_script)))                      # Script length
        s += tx_script
    # Change output.
    if change_output:
        change_address, change_value = change_output
        s += change_value.to_bytes(8, byteorder='little')   # Value
        tx_script, witness_script = get_script(change_address)
        #print("Change address!", change_address, "\n", witnessM
_data, "\n", tx_script, "\n", witness_script)
        #if witness_script: #use_segwit and change_address in witness_data:
        #    if not(change_address in witness_data):
        #        witness_data[change_address] = []
        #    witness_data[change_address].append(witness_script)
        #    tx_script = witness_script
        #    use_segwit = True
        s += var_int(int(len(tx_script)))                      # Script length
        s += tx_script
        for i in range(len(inputs))M
            txin = inputs[i]
            if txin['is_segwit']:
                s += b'\x02'
                s += b'\x00\x00'
                s += b'\x00'
    s += (0).to_bytes(4, byteorder='little')                # LockTime
def serialise_p2sh_pretx(inputs, source, source_value, data_output, change_output=None, pubkey=None, multisig_pubkeys=None, multisig_pubkeys_required=None):
    assert data_output  # we don't do this unless there's data
    data_array, data_value = data_outM
    s  = (1).to_bytes(4, byteorder='little')  # Version
    # Number of inputs.
    s += var_int(int(len(inputs)))
    # List of Inputs.
    for i in range(len(inputs)):
        txin = inputs[i]
        s += binascii.unhexlify(bytes(txin['txid'], 'utf-8'))[::-1]  # TxOutHash
        s += txin['vout'].to_bytes(4, byteorder='little')            # TxOutIndex
        tx_script = binascii.unhexlify(bytes(txin['scriptPubKey'], 'utf-8'))
        s += var_int(int(len(tx_script)))  # Script length
ript                     # Script
        s += b'\xff' * 4                   # Sequence
    # Number of outputs.
    n = len(data_array)
    if change_output:
    # encode number of outputs
    # P2SH for data encodeded inputs
    for n, data_chunk in enumerate(data_array):
        data_chunk = config.PREFIX + data_chunk  # prefix the data_chunk
        # get the scripts
        scriptSig, redeemScript, outputScript = p2sh_encoding.make_p2sh_encoding_redeemscript(data_chunk, nM
, pubkey, multisig_pubkeys, multisig_pubkeys_required)
        #if data_value is an array, then every output fee is specified in it
        if type(data_value) == list:
            s += data_value[n].to_bytes(8, byteorder='little')  # Value
            s += data_value.to_bytes(8, byteorder='little')  # Value
        s += var_int(int(len(outputScript)))             # Script length
        s += outputScript                                # Script
    # Change output.
    if change_output:
ge_address, change_value = change_output
        tx_script, witness_script = get_script(change_address)
        s += change_value.to_bytes(8, byteorder='little')  # Value
        s += var_int(int(len(tx_script)))                  # Script length
        s += tx_script                                     # Script
    s += (0).to_bytes(4, byteorder='little')  # LockTime
def serialise_p2sh_datatx(txid, source, source_input, destination_outputs, data_output, pubkey=None, multisig_pubkeys=None, multisiM
g_pubkeys_required=None):
    assert data_output  # we don't do this unless there's data
    txhash = bitcoinlib.core.lx(bitcoinlib.core.b2x(txid))  # reverse txId
    data_array, value = data_output
    s = (1).to_bytes(4, byteorder='little')
    # number of inputs is the length of data_array (+1 if a source_input exists)
    number_of_inputs = len(data_array)
    if source_input is not None:
        number_of_inputs += 1
    s += var_int(number_of_inputs)
    # Handle a source input here for a PM
    if source_input is not None:
        s += binascii.unhexlify(bytes(source_input['txid'], 'utf-8'))[::-1]  # TxOutHash
        s += source_input['vout'].to_bytes(4, byteorder='little')            # TxOutIndex
        # since pubkey is not returned from indexd, add it from bitcoind
        source_inputs = backend.ensure_script_pub_key_for_inputs([source_input])
        source_input = source_inputs[0]
        tx_script = binascii.unhexlify(bytes(source_input['scriptPubKey'], 'utf-8'))
int(int(len(tx_script)))                                    # Script length
        s += tx_script                                                       # Script
        s += b'\xff' * 4                                                     # Sequence
    # list of inputs
    for n, data_chunk in enumerate(data_array):
        data_chunk = config.PREFIX + data_chunk  # prefix the data_chunk
        # get the scripts
        scriptSig, redeemScript, outputScript = p2sh_encoding.make_p2sh_encoding_redeemscript(data_cM
hunk, n, pubkey, multisig_pubkeys, multisig_pubkeys_required)
        #substituteScript = scriptSig + outputScript
        s += txhash                                              # TxOutHash
        s += (n).to_bytes(4, byteorder='little')                 # TxOutIndex (assumes 0-based)
        #s += var_int(len(substituteScript))                      # Script length
        #s += substituteScript                                    # Script
        s += var_int(len(scriptSig))# + len(outputScript))              M
        # Script length
        s += scriptSig                                    # Script
        #s += outputScript                                    # Script
        s += b'\xff' * 4                                         # Sequence
    # number of outputs, always 1 for the opreturn
    n += len(destination_outputs)
    # encode output length
    # destination outputs
    for destination, value in destination_outputs:
        tx_script, witness_script = get_script(destination)M
        s += value.to_bytes(8, byteorder='little')  # Value
        s += var_int(int(len(tx_script)))           # Script length
        s += tx_script                              # Script
    # opreturn to signal P2SH encoding
    key = arc4.init_arc4(txid)
    data_chunk = config.PREFIX + b'P2SH'
    data_chunk = key.encrypt(data_chunk)
    tx_script = OP_RETURN                  # OP_RETURN
    tx_script += op_push(len(data_chunk))  # Push bytes of data chunk
    tx_script += data_chunk                # Data
    s += (0).to_bytes(8, byteorder='little')  # Value
    s += var_int(int(len(tx_script)))         # Script length
    s += tx_script                            # Script
    s += (0).to_bytes(4, byteorder='little')  # LockTime
UUUUUUUUUUUUUUUUUUUUUUUUUUUUU
!22222222222222222222222222222222222222222222222222
text/plain;charset=utf-8
# Merged Python Files
Datastreams are identified by the address that publishes them, and referenced
in transaction outputs.
For CFD leverage, 1x = 5040, 2x = 10080, etc.: 5040 is a superior highly
composite number and a colossally abundant number, and has 1-10, 12 as factors.
All wagers are in XCP.
Expiring a bet match doesn
open the constituent bets. (So all bets may be
logger = logging.getLogger(__name__)
from counterpartylib.lib import config
from counterpartylib.lib import exceptions
from counterpartylib.lib import util
from counterpartylib.lib import log
from counterpartylib.lib import message_type
LENGTH = 2 + 4 + 8 + 8 + 8 + 4 + 4
def initialise (db):
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS bets(
                      tx_index INTEGER UNIQUE,
                      tx_hash TEXT M
                      block_index INTEGER,
                      source TEXT,
                      feed_address TEXT,
                      bet_type INTEGER,
                      deadline INTEGER,
                      wager_quantity INTEGER,
                      wager_remaining INTEGER,
                      counterwager_quantity INTEGER,
                      counterwager_remaining INTEGER,
                      target_value REAL,
                      leverage INTEGER,
                      expirationM
                      expire_index INTEGER,
                      fee_fraction_int INTEGER,
                      status TEXT,
                      FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index),
                      PRIMARY KEY (tx_index, tx_hash))
                  ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON bets (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTSM
                      index_hash_idx ON bets (tx_index, tx_hash)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      expire_idx ON bets (status, expire_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      feed_valid_bettype_idx ON bets (feed_address, status, bet_type)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON bets (source)
                   ''')
execute('''CREATE INDEX IF NOT EXISTS
                      status_idx ON bets (status)
                   ''')
    cursor.execute('''CREATE TABLE IF NOT EXISTS bet_matches(
                      id TEXT PRIMARY KEY,
                      tx0_index INTEGER,
                      tx0_hash TEXT,
                      tx0_address TEXT,
                      tx1_index INTEGER,
                      tx1_hash TEXT,
                      tx1_address TEXT,
                      tx0_bet_type INTEGER,
                  tx1_bet_type INTEGER,
                      feed_address TEXT,
                      initial_value INTEGER,
                      deadline INTEGER,
                      target_value REAL,
                      leverage INTEGER,
                      forward_quantity INTEGER,
                      backward_quantity INTEGER,
                      tx0_block_index INTEGER,
                      tx1_block_index INTEGER,
                      block_index INTEGER,
                      tx0_expiration INM
                      tx1_expiration INTEGER,
                      match_expire_index INTEGER,
                      fee_fraction_int INTEGER,
                      status TEXT,
                      FOREIGN KEY (tx0_index, tx0_hash, tx0_block_index) REFERENCES transactions(tx_index, tx_hash, block_index),
                      FOREIGN KEY (tx1_index, tx1_hash, tx1_block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                     match_expire_idx ON bet_matches (status, match_expire_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      valid_feed_idx ON bet_matches (feed_address, status)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      id_idx ON bet_matches (id)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      tx0_address_idx ON bet_matches (tx0_address)
                   ''')
cursor.execute('''CREATE INDEX IF NOT EXISTS
                      tx1_address_idx ON bet_matches (tx1_address)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      status_idx ON bet_matches (status)
                   ''')
    # Bet Expirations
    cursor.execute('''CREATE TABLE IF NOT EXISTS bet_expirations(
                      bet_index INTEGER PRIMARY KEY,
                      bet_hash TEXT UNIQUE,
                      source TEXT,
                      block_indexM
                      FOREIGN KEY (block_index) REFERENCES blocks(block_index),
                      FOREIGN KEY (bet_index, bet_hash) REFERENCES bets(tx_index, tx_hash))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON bet_expirations (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON bet_expirations (source)
                   ''')
    # Bet Match Expirations
sor.execute('''CREATE TABLE IF NOT EXISTS bet_match_expirations(
                      bet_match_id TEXT PRIMARY KEY,
                      tx0_address TEXT,
                      tx1_address TEXT,
                      block_index INTEGER,
                      FOREIGN KEY (bet_match_id) REFERENCES bet_matches(id),
                      FOREIGN KEY (block_index) REFERENCES blocks(block_index))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON bet_matcM
h_expirations (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      tx0_address_idx ON bet_match_expirations (tx0_address)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      tx1_address_idx ON bet_match_expirations (tx1_address)
                   ''')
    # Bet Match Resolutions
    cursor.execute('''CREATE TABLE IF NOT EXISTS bet_match_resolutions(
                      bet_match_id TEXT PRIMARY KEY,
        bet_match_type_id INTEGER,
                      block_index INTEGER,
                      winner TEXT,
                      settled BOOL,
                      bull_credit INTEGER,
                      bear_credit INTEGER,
                      escrow_less_fee INTEGER,
                      fee INTEGER,
                      FOREIGN KEY (bet_match_id) REFERENCES bet_matches(id),
                      FOREIGN KEY (block_index) REFERENCES blocks(block_index))
                   ''')
def cancel_bet (db, bM
et, status, block_index):
    cursor = db.cursor()
    # Update status of bet.
        'status': status,
        'tx_hash': bet['tx_hash']
    sql='update bets set status = :status where tx_hash = :tx_hash'
    cursor.execute(sql, bindings)
    log.message(db, block_index, 'update', 'bets', bindings)
    util.credit(db, bet['source'], config.XCP, bet['wager_remaining'], action='recredit wager remaining', event=bet['tx_hash'])
    cursor = db.cursor()
def cancel_bet_match (db, bet_match, sM
tatus, block_index):
fill, etc. constituent bets.
    cursor = db.cursor()
    # Recredit tx0 address.
    util.credit(db, bet_match['tx0_address'], config.XCP,
                bet_match['forward_quantity'], action='recredit forward quantity', event=bet_match['id'])
    # Recredit tx1 address.
    util.credit(db, bet_match['tx1_address'], config.XCP,
                bet_match['backward_quantity'], action='recredit backward quantity', event=bet_match['id'])
    # Update status of bM
        'status': status,
        'bet_match_id': bet_match['id']
    sql='update bet_matches set status = :status where id = :bet_match_id'
    cursor.execute(sql, bindings)
    log.message(db, block_index, 'update', 'bet_matches', bindings)
def get_fee_fraction (db, feed_address):
    '''Get fee fraction from last broadcast from the feed_address address.
    cursor = db.cursor()
    broadcasts = list(cursor.execute('''SELECT * FROM broadcasts WHERE (sM
tatus = ? AND source = ?) ORDER BY tx_index ASC''', ('valid', feed_address)))
        last_broadcast = broadcasts[-1]
        fee_fraction_int = last_broadcast['fee_fraction_int']
        if fee_fraction_int: return fee_fraction_int / 1e8
        else: return 0
def validate (db, source, feed_address, bet_type, deadline, wager_quantity,
              counterwager_quantity, target_value, leverage, expiration, block_index):
erage is None: leverage = 5040
    if wager_quantity > config.MAX_INT or counterwager_quantity > config.MAX_INT or bet_type > config.MAX_INT \
            or deadline > config.MAX_INT or leverage > config.MAX_INT or block_index + expiration > config.MAX_INT:
        problems.append('integer overflow')
    # Look at feed to be bet on.
    cursor = db.cursor()
    broadcasts = list(cursor.execute('''SELECT * FROM broadcasts WHERE (status = ? AND source = ?) ORDER BY tx_index ASC''', ('valid', feedM
    if not broadcasts:
        problems.append('feed doesn
    elif not broadcasts[-1]['text']:
        problems.append('feed is locked')
    elif broadcasts[-1]['timestamp'] >= deadline:
        problems.append('deadline in that feed
    if not bet_type in (0, 1, 2, 3):
        problems.append('unknown bet type')
    # Valid leverage level?
    if leverage != 5040 and bet_type in (2,3):   # Equal, NotEqual
        problems.append('leverage used with Equal or M
    if leverage < 5040 and not bet_type in (0,1):   # BullCFD, BearCFD (fractional leverage makes sense precisely with CFDs)
        problems.append('leverage level too low')
    if bet_type in (0,1):   # BullCFD, BearCFD
        if block_index >= 312350:   # Protocol change.
            problems.append('CFDs temporarily disabled')
    if not isinstance(wager_quantity, int):
        problems.append('wager_quantity must be in satoshis')
        return problems, leverage
    if not isinstance(counterwageM
        problems.append('counterwager_quantity must be in satoshis')
        return problems, leverage
    if not isinstance(expiration, int):
        problems.append('expiration must be expressed as an integer block delta')
        return problems, leverage
    if wager_quantity <= 0: problems.append('non
    if counterwager_quantity <= 0: problems.append('non
positive counterwager')
    if deadline < 0: problems.append('negative deadline')
    if expiration < 0: problems.apM
pend('negative expiration')
    if expiration == 0 and not (block_index >= 317500 or config.TESTNET or config.REGTEST):   # Protocol change.
        problems.append('zero expiration')
    if target_value:
        if bet_type in (0,1):   # BullCFD, BearCFD
            problems.append('CFDs have no target value')
        if target_value < 0:
            problems.append('negative target value')
    if expiration > config.MAX_EXPIRATION:
        problems.append('expiration overflow')
    return problems, leverage
ef compose (db, source, feed_address, bet_type, deadline, wager_quantity,
            counterwager_quantity, target_value, leverage, expiration):
    if util.get_balance(db, source, config.XCP) < wager_quantity:
        raise exceptions.ComposeError('insufficient funds')
    problems, leverage = validate(db, source, feed_address, bet_type, deadline, wager_quantity,
                        counterwager_quantity, target_value, leverage, expiration, util.CURRENT_BLOCK_INDEX)
    if util.date_passed(deadline):
  problems.append('deadline passed')
    if problems: raise exceptions.ComposeError(problems)
    data = message_type.pack(ID)
    data += struct.pack(FORMAT, bet_type, deadline,
                        wager_quantity, counterwager_quantity, target_value,
                        leverage, expiration)
    return (source, [(feed_address, None)], data)
def parse (db, tx, message):
    bet_parse_cursor = db.cursor()
    # Unpack message.
        if len(message) != LENGTH:
            raise exceptions.UnpackM
        (bet_type, deadline, wager_quantity,
         counterwager_quantity, target_value, leverage,
         expiration) = struct.unpack(FORMAT, message)
        status = 'open'
    except (exceptions.UnpackError, struct.error):
        (bet_type, deadline, wager_quantity,
         counterwager_quantity, target_value, leverage,
         expiration, fee_fraction_int) = 0, 0, 0, 0, 0, 0, 0, 0
        status = 'invalid: could not unpack'
    odds, fee_fraction = 0, 0
    feed_address = tx['destination']
            odds = util.price(wager_quantity, counterwager_quantity)
        except ZeroDivisionError:
            odds = 0
        fee_fraction = get_fee_fraction(db, feed_address)
        bet_parse_cursor.execute('''SELECT * FROM balances \
                                    WHERE (address = ? AND asset = ?)''', (tx['source'], config.XCP))
        balances = list(bet_parse_cursor)
        if not balances:
            wager_quantity = 0
 balance = balances[0]['quantity']
            if balance < wager_quantity:
                wager_quantity = balance
                counterwager_quantity = int(util.price(wager_quantity, odds))
        problems, leverage = validate(db, tx['source'], feed_address, bet_type, deadline, wager_quantity,
                            counterwager_quantity, target_value, leverage, expiration, tx['block_index'])
        if problems: status = 'invalid: ' + '; '.join(problems)
    # Debit quantity wagered. (Escrow.)
        util.debit(db, tx['source'], config.XCP, wager_quantity, action='bet', event=tx['tx_hash'])
    # Add parsed transaction to message-type
        'tx_index': tx['tx_index'],
        'tx_hash': tx['tx_hash'],
        'block_index': tx['block_index'],
        'source': tx['source'],
        'feed_address': feed_address,
        'bet_type': bet_type,
        'deadline': deadline,
        'wager_quantity': wager_quantity,
        'wager_remaining': wager_quanM
        'counterwager_quantity': counterwager_quantity,
        'counterwager_remaining': counterwager_quantity,
        'target_value': target_value,
        'leverage': leverage,
        'expiration': expiration,
        'expire_index': tx['block_index'] + expiration,
        'fee_fraction_int': fee_fraction * 1e8,
        'status': status,
    if "integer overflow" not in status:
        sql = 'insert into bets values(:tx_index, :tx_hash, :block_index, :source, :feed_address, :bet_type, :deadline, :wM
ager_quantity, :wager_remaining, :counterwager_quantity, :counterwager_remaining, :target_value, :leverage, :expiration, :expire_index, :fee_fraction_int, :status)'
        bet_parse_cursor.execute(sql, bindings)
        logger.warn("Not storing [bet] tx [%s]: %s" % (tx['tx_hash'], status))
        logger.debug("Bindings: %s" % (json.dumps(bindings), ))
    if status == 'open' and tx['block_index'] != config.MEMPOOL_BLOCK_INDEX:
        match(db, tx)
    bet_parse_cursor.close()
    cursor = db.cursor()
    # Get bet in question.
    bets = list(cursor.execute('''SELECT * FROM bets\
                                  WHERE (tx_index = ? AND status = ?)''', (tx['tx_index'], 'open')))
        cursor.close()
        assert len(bets) == 1
    # Get counterbet_type.
    if tx1['bet_type'] % 2: counterbet_type = tx1['bet_type'] - 1
    else: counterbet_type = tx1['bet_type'] + 1
    feed_address = tx1['feed_address']
ursor.execute('''SELECT * FROM bets\
                             WHERE (feed_address=? AND status=? AND bet_type=?)''',
                             (tx1['feed_address'], 'open', counterbet_type))
    tx1_wager_remaining = tx1['wager_remaining']
    tx1_counterwager_remaining = tx1['counterwager_remaining']
    bet_matches = cursor.fetchall()
    if tx['block_index'] > 284500 or config.TESTNET or config.REGTEST:  # Protocol change.
        sorted(bet_matches, key=lambda x: x['tx_index'])                           M
             # Sort by tx index second.
        sorted(bet_matches, key=lambda x: util.price(x['wager_quantity'], x['counterwager_quantity']))   # Sort by price first.
    tx1_status = tx1['status']
    for tx0 in bet_matches:
        if tx1_status != 'open': break
        logger.debug('Considering: ' + tx0['tx_hash'])
        tx0_wager_remaining = tx0['wager_remaining']
        tx0_counterwager_remaining = tx0['counterwager_remaining']
        # Bet types must be opposite.
        if counterbet_type != tx0['betM
            logger.debug('Skipping: bet types disagree.')
            continue
        # Leverages must agree exactly
        if tx0['leverage'] != tx1['leverage']:
            logger.debug('Skipping: leverages disagree.')
            continue
        # Target values must agree exactly.
        if tx0['target_value'] != tx1['target_value']:
            logger.debug('Skipping: target values disagree.')
            continue
        # Fee fractions must agree exactly.
        if tx0['fee_fraction_int'] != M
tx1['fee_fraction_int']:
            logger.debug('Skipping: fee fractions disagree.')
            continue
        # Deadlines must agree exactly.
        if tx0['deadline'] != tx1['deadline']:
            logger.debug('Skipping: deadlines disagree.')
            continue
        # If the odds agree, make the trade. The found order sets the odds,
        # and they trade as much as they can.
        tx0_odds = util.price(tx0['wager_quantity'], tx0['counterwager_quantity'])
        tx0_inverse_odds = util.price(tM
x0['counterwager_quantity'], tx0['wager_quantity'])
        tx1_odds = util.price(tx1['wager_quantity'], tx1['counterwager_quantity'])
        if tx['block_index'] < 286000: tx0_inverse_odds = util.price(1, tx0_odds) # Protocol change.
        logger.debug('Tx0 Inverse Odds: {}; Tx1 Odds: {}'.format(float(tx0_inverse_odds), float(tx1_odds)))
        if tx0_inverse_odds > tx1_odds:
            logger.debug('Skipping: price mismatch.')
            logger.debug('Potential forward quantities: {}, {}'.foM
rmat(tx0_wager_remaining, int(util.price(tx1_wager_remaining, tx1_odds))))
            forward_quantity = int(min(tx0_wager_remaining, int(util.price(tx1_wager_remaining, tx1_odds))))
            logger.debug('Forward Quantity: {}'.format(forward_quantity))
            backward_quantity = round(forward_quantity / tx0_odds)
            logger.debug('Backward Quantity: {}'.format(backward_quantity))
            if not forward_quantity:
                logger.debug('Skipping: zero forward quantity.')
            if tx1['block_index'] >= 286500 or config.TESTNET or config.REGTEST:    # Protocol change.
                if not backward_quantity:
                    logger.debug('Skipping: zero backward quantity.')
                    continue
            bet_match_id = util.make_id(tx0['tx_hash'], tx1['tx_hash'])
            # Debit the order.
            # Counterwager remainings may be negative.
            tx0_wager_remaining = tx0_wager_remaining - forward_quantity
            tx0_counterwager_remaiM
ning = tx0_counterwager_remaining - backward_quantity
            tx1_wager_remaining = tx1_wager_remaining - backward_quantity
            tx1_counterwager_remaining = tx1_counterwager_remaining - forward_quantity
            tx0_status = 'open'
            if tx0_wager_remaining <= 0 or tx0_counterwager_remaining <= 0:
                # Fill order, and recredit give_remaining.
                tx0_status = 'filled'
                util.credit(db, tx0['source'], config.XCP, tx0_wager_remaining, eM
vent=tx1['tx_hash'], action='filled')
            bindings = {
                'wager_remaining': tx0_wager_remaining,
                'counterwager_remaining': tx0_counterwager_remaining,
                'status': tx0_status,
                'tx_hash': tx0['tx_hash']
            sql='update bets set wager_remaining = :wager_remaining, counterwager_remaining = :counterwager_remaining, status = :status where tx_hash = :tx_hash'
            cursor.execute(sql, bindings)
            log.message(db, tx['bM
lock_index'], 'update', 'bets', bindings)
            if tx1['block_index'] >= 292000 or config.TESTNET or config.REGTEST:  # Protocol change
                if tx1_wager_remaining <= 0 or tx1_counterwager_remaining <= 0:
                    # Fill order, and recredit give_remaining.
                    tx1_status = 'filled'
                    util.credit(db, tx1['source'], config.XCP, tx1_wager_remaining, event=tx1['tx_hash'], action='filled')
            bindings = {
                'wager_remM
aining': tx1_wager_remaining,
                'counterwager_remaining': tx1_counterwager_remaining,
                'status': tx1_status,
                'tx_hash': tx1['tx_hash']
            sql='update bets set wager_remaining = :wager_remaining, counterwager_remaining = :counterwager_remaining, status = :status where tx_hash = :tx_hash'
            cursor.execute(sql, bindings)
            log.message(db, tx['block_index'], 'update', 'bets', bindings)
            # Get last value of feed.
    broadcasts = list(cursor.execute('''SELECT * FROM broadcasts WHERE (status = ? AND source = ?) ORDER BY tx_index ASC''', ('valid', feed_address)))
            initial_value = broadcasts[-1]['value']
            # Record bet fulfillment.
            bindings = {
                'id': util.make_id(tx0['tx_hash'], tx['tx_hash']),
                'tx0_index': tx0['tx_index'],
                'tx0_hash': tx0['tx_hash'],
                'tx0_address': tx0['source'],
                'tx1_index': tx1['tx_index'],
            'tx1_hash': tx1['tx_hash'],
                'tx1_address': tx1['source'],
                'tx0_bet_type': tx0['bet_type'],
                'tx1_bet_type': tx1['bet_type'],
                'feed_address': tx1['feed_address'],
                'initial_value': initial_value,
                'deadline': tx1['deadline'],
                'target_value': tx1['target_value'],
                'leverage': tx1['leverage'],
                'forward_quantity': forward_quantity,
                'backward_quantity': bM
                'tx0_block_index': tx0['block_index'],
                'tx1_block_index': tx1['block_index'],
                'block_index': max(tx0['block_index'], tx1['block_index']),
                'tx0_expiration': tx0['expiration'],
                'tx1_expiration': tx1['expiration'],
                'match_expire_index': min(tx0['expire_index'], tx1['expire_index']),
                'fee_fraction_int': tx1['fee_fraction_int'],
                'status': 'pending',
ql='insert into bet_matches values(:id, :tx0_index, :tx0_hash, :tx0_address, :tx1_index, :tx1_hash, :tx1_address, :tx0_bet_type, :tx1_bet_type, :feed_address, :initial_value, :deadline, :target_value, :leverage, :forward_quantity, :backward_quantity, :tx0_block_index, :tx1_block_index, :block_index, :tx0_expiration, :tx1_expiration, :match_expire_index, :fee_fraction_int, :status)'
            cursor.execute(sql, bindings)
def expire (db, block_index, block_time):
    cursor = db.curM
    # Expire bets and give refunds for the quantity wager_remaining.
    cursor.execute('''SELECT * FROM bets \
                      WHERE (status = ? AND expire_index < ?)''', ('open', block_index))
    for bet in cursor.fetchall():
        cancel_bet(db, bet, 'expired', block_index)
        # Record bet expiration.
        bindings = {
            'bet_index': bet['tx_index'],
            'bet_hash': bet['tx_hash'],
            'source': bet['source'],
            'block_index': block_index
     sql='insert into bet_expirations values(:bet_index, :bet_hash, :source, :block_index)'
        cursor.execute(sql, bindings)
    # Expire bet matches whose deadline is more than two weeks before the current block time.
    cursor.execute('''SELECT * FROM bet_matches \
                      WHERE (status = ? AND deadline < ?)''', ('pending', block_time - config.TWO_WEEKS))
    for bet_match in cursor.fetchall():
        cancel_bet_match(db, bet_match, 'expired', block_index)
        # Record bet match expiratM
        bindings = {
            'bet_match_id': bet_match['id'],
            'tx0_address': bet_match['tx0_address'],
            'tx1_address': bet_match['tx1_address'],
            'block_index': block_index
        sql='insert into bet_match_expirations values(:bet_match_id, :tx0_address, :tx1_address, :block_index)'
        cursor.execute(sql, bindings)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
sage, with or without a price.
Multiple messages per block are allowed. Bets are be made on the 'timestamp'
field, and not the block index.
An address is a feed of broadcasts. Feeds may be locked with a broadcast whose
text field is identical to
 (case insensitive). Bets on a feed reference
the address that is the source of the feed in an output which includes the
(latest) required fee.
Broadcasts without a price may not be used for betting. Broadcasts about events
with a small number of possible outcoM
mes (e.g. sports games), should be
written, for example, such that a price of 1 XCP means one outcome, 2 XCP means
another, etc., which schema should be described in the 'text' field.
fee_fraction: .05 XCP means 5%. It may be greater than 1, however; but
because it is stored as a four
byte integer, it may not be greater than about
from fractions import Fraction
logger = logging.getLogger(__name__)
from bitcoin.core import VarInM
from counterpartylib.lib import exceptions
from counterpartylib.lib import config
from counterpartylib.lib import util
from counterpartylib.lib import log
from counterpartylib.lib import message_type
# NOTE: Pascal strings are used for storing texts for backwards
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS broadcasts(
                      tx_index INTEGER PRIMARY KEY,
                   tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      source TEXT,
                      timestamp INTEGER,
                      value REAL,
                      fee_fraction_int INTEGER,
                      text TEXT,
                      locked BOOL,
                      status TEXT,
                      FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
    cursor.execute('''CREATEM
 INDEX IF NOT EXISTS
                      block_index_idx ON broadcasts (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      status_source_idx ON broadcasts (status, source)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      status_source_index_idx ON broadcasts (status, source, tx_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      timestamp_idx ON broadcasts (timesM
                   ''')
def validate (db, source, timestamp, value, fee_fraction_int, text, block_index):
    if timestamp > config.MAX_INT or value > config.MAX_INT or fee_fraction_int > config.MAX_INT:
        problems.append('integer overflow')
    if util.enabled('max_fee_fraction'):
        if fee_fraction_int >= config.UNIT:
            problems.append('fee fraction greater than or equal to 1')
        if fee_fraction_int > 4294967295:
            problemM
s.append('fee fraction greater than 42.94967295')
    if timestamp < 0: problems.append('negative timestamp')
        problems.append('null source address')
    # Check previous broadcast in this feed.
    cursor = db.cursor()
    broadcasts = list(cursor.execute('''SELECT * FROM broadcasts WHERE (status = ? AND source = ?) ORDER BY tx_index ASC''', ('valid', source)))
        last_broadcast = broadcasts[-1]
        if last_broadcast['locked']:
problems.append('locked feed')
        elif timestamp <= last_broadcast['timestamp']:
            problems.append('feed timestamps not monotonically increasing')
    if not (block_index >= 317500 or config.TESTNET or config.REGTEST):  # Protocol change.
        if len(text) > 52:
            problems.append('text too long')
    if util.enabled('options_require_memo') and text and text.lower().startswith('options'):
            # Check for options and if they are valid.
            options = util.parsM
e_options_from_string(text)
            if options is not False:
                util.validate_address_options(options)
        except exceptions.OptionsError as e:
            problems.append(str(e))
def compose (db, source, timestamp, value, fee_fraction, text):
    # Store the fee fraction as an integer.
    fee_fraction_int = int(fee_fraction * 1e8)
    problems = validate(db, source, timestamp, value, fee_fraction_int, text, util.CURRENT_BLOCK_INDEX)
    if problems: raise exceptions.CoM
mposeError(problems)
    data = message_type.pack(ID)
    # always use custom length byte instead of problematic usage of 52p format and make sure to encode('utf-8') for length
    if util.enabled('broadcast_pack_text'):
        data += struct.pack(FORMAT, timestamp, value, fee_fraction_int)
        data += VarIntSerializer.serialize(len(text.encode('utf-8')))
        data += text.encode('utf-8')
        if len(text) <= 52:
            curr_format = FORMAT + '{}p'.format(len(text) + 1)
         curr_format = FORMAT + '{}s'.format(len(text))
        data += struct.pack(curr_format, timestamp, value, fee_fraction_int, text.encode('utf-8'))
    return (source, [], data)
def parse (db, tx, message):
    cursor = db.cursor()
    # Unpack message.
        if util.enabled('broadcast_pack_text', tx['block_index']):
            timestamp, value, fee_fraction_int, rawtext = struct.unpack(FORMAT + '{}s'.format(len(message) - LENGTH), message)
            textlen = VarIntSerializer.deserialize(raM
            if textlen == 0:
                text = b''
                text = rawtext[-textlen:]
            assert len(text) == textlen
            if len(message) - LENGTH <= 52:
                curr_format = FORMAT + '{}p'.format(len(message) - LENGTH)
                curr_format = FORMAT + '{}s'.format(len(message) - LENGTH)
            timestamp, value, fee_fraction_int, text = struct.unpack(curr_format, message)
            text = text.M
        except UnicodeDecodeError:
            text = ''
        status = 'valid'
    except (struct.error) as e:
        timestamp, value, fee_fraction_int, text = 0, None, 0, None
        status = 'invalid: could not unpack'
    if status == 'valid':
        # For SQLite3
        timestamp = min(timestamp, config.MAX_INT)
        value = min(value, config.MAX_INT)
        problems = validate(db, tx['source'], timestamp, value, fee_fraction_int, text, tx['block_index'])
        if problems: statuM
s = 'invalid: ' + '; '.join(problems)
    if text and text.lower() == 'lock':
        timestamp, value, fee_fraction_int, text = 0, None, None, None
        lock = False
    # Add parsed transaction to message-type
        'tx_index': tx['tx_index'],
        'tx_hash': tx['tx_hash'],
        'block_index': tx['block_index'],
        'source': tx['source'],
        'timestamp': timestamp,
        'value': value,
fee_fraction_int': fee_fraction_int,
        'text': text,
        'locked': lock,
        'status': status,
    if "integer overflow" not in status:
        sql = 'insert into broadcasts values(:tx_index, :tx_hash, :block_index, :source, :timestamp, :value, :fee_fraction_int, :text, :locked, :status)'
        cursor.execute(sql, bindings)
        logger.warn("Not storing [broadcast] tx [%s]: %s" % (tx['tx_hash'], status))
        logger.debug("Bindings: %s" % (json.dumps(bindings), ))
rocessing if broadcast is invalid for any reason
    if util.enabled('broadcast_invalid_check') and status != 'valid':
    # Options? Should not fail to parse due to above checks.
    if util.enabled('options_require_memo') and text and text.lower().startswith('options'):
        options = util.parse_options_from_string(text)
        if options is not False:
            op_bindings = {
                        'block_index': tx['block_index'],
                        'address': tx['source'],
                'options': options
                       }
            sql = 'insert or replace into addresses(address, options, block_index) values(:address, :options, :block_index)'
            cursor = db.cursor()
            cursor.execute(sql, op_bindings)
    # Negative values (default to ignore).
    if value is None or value < 0:
        # Cancel Open Bets?
        if value == -2:
            cursor.execute('''SELECT * FROM bets \
                              WHERE (status = ? AND feed_address = ?)''',
                          ('open', tx['source']))
            for i in list(cursor):
                bet.cancel_bet(db, i, 'dropped', tx['block_index'])
        # Cancel Pending Bet Matches?
        if value == -3:
            cursor.execute('''SELECT * FROM bet_matches \
                              WHERE (status = ? AND feed_address = ?)''',
                           ('pending', tx['source']))
            for bet_match in list(cursor):
                bet.cancel_bet_match(db, bet_match, 'dropped', tx['block_indM
        cursor.close()
    # stop processing if broadcast is invalid for any reason
    # @TODO: remove this check once broadcast_invalid_check has been activated
    if util.enabled('max_fee_fraction') and status != 'valid':
    # Handle bet matches that use this feed.
    cursor.execute('''SELECT * FROM bet_matches \
                      WHERE (status=? AND feed_address=?)
                      ORDER BY tx1_index ASC, tx0_index ASC''',
                   ('pending', tx['sourM
    for bet_match in cursor.fetchall():
        broadcast_bet_match_cursor = db.cursor()
        bet_match_id = util.make_id(bet_match['tx0_hash'], bet_match['tx1_hash'])
        bet_match_status = None
        # Calculate total funds held in escrow and total fee to be paid if
        # the bet match is settled. Escrow less fee is amount to be paid back
        # to betters.
        total_escrow = bet_match['forward_quantity'] + bet_match['backward_quantity']
        if util.enabled('inmutable_fee_fractionM
            fee_fraction = bet_match['fee_fraction_int'] / config.UNIT
            fee_fraction = fee_fraction_int / config.UNIT
        fee = int(fee_fraction * total_escrow)              # Truncate.
        escrow_less_fee = total_escrow - fee
        # Get known bet match type IDs.
        cfd_type_id = util.BET_TYPE_ID['BullCFD'] + util.BET_TYPE_ID['BearCFD']
        equal_type_id = util.BET_TYPE_ID['Equal'] + util.BET_TYPE_ID['NotEqual']
        # Get the bet match type ID of this bet matcM
        bet_match_type_id = bet_match['tx0_bet_type'] + bet_match['tx1_bet_type']
        # Contract for difference, with determinate settlement date.
        if bet_match_type_id == cfd_type_id:
            # Recognise tx0, tx1 as the bull, bear (in the right direction).
            if bet_match['tx0_bet_type'] < bet_match['tx1_bet_type']:
                bull_address = bet_match['tx0_address']
                bear_address = bet_match['tx1_address']
                bull_escrow = bet_match['forward_quantity']
                bear_escrow = bet_match['backward_quantity']
                bull_address = bet_match['tx1_address']
                bear_address = bet_match['tx0_address']
                bull_escrow = bet_match['backward_quantity']
                bear_escrow = bet_match['forward_quantity']
            leverage = Fraction(bet_match['leverage'], 5040)
            initial_value = bet_match['initial_value']
            bear_credit = bear_escrow - (value - initial_value) * leverage * config.UNIT
           bull_credit = escrow_less_fee - bear_credit
            bear_credit = round(bear_credit)
            bull_credit = round(bull_credit)
            # Liquidate, as necessary.
            if bull_credit >= escrow_less_fee or bull_credit <= 0:
                if bull_credit >= escrow_less_fee:
                    bull_credit = escrow_less_fee
                    bear_credit = 0
                    bet_match_status = 'settled: liquidated for bull'
                    util.credit(db, bull_address, config.XCP,M
 bull_credit, action='bet {}'.format(bet_match_status), event=tx['tx_hash'])
                elif bull_credit <= 0:
                    bull_credit = 0
                    bear_credit = escrow_less_fee
                    bet_match_status = 'settled: liquidated for bear'
                    util.credit(db, bear_address, config.XCP, bear_credit, action='bet {}'.format(bet_match_status), event=tx['tx_hash'])
                # Pay fee to feed.
                util.credit(db, bet_match['feed_address'], config.XCP, feeM
, action='feed fee', event=tx['tx_hash'])
                # For logging purposes.
                bindings = {
                    'bet_match_id': bet_match_id,
                    'bet_match_type_id': bet_match_type_id,
                    'block_index': tx['block_index'],
                    'settled': False,
                    'bull_credit': bull_credit,
                    'bear_credit': bear_credit,
                    'winner': None,
                    'escrow_less_fee': None,
                    'fee': feM
                sql='insert into bet_match_resolutions values(:bet_match_id, :bet_match_type_id, :block_index, :settled, :bull_credit, :bear_credit, :winner, :escrow_less_fee, :fee)'
                cursor.execute(sql, bindings)
            # Settle (if not liquidated).
            elif timestamp >= bet_match['deadline']:
                bet_match_status = 'settled'
                util.credit(db, bull_address, config.XCP, bull_credit, action='bet {}'.format(bet_match_status), event=tx['tx_hasM
                util.credit(db, bear_address, config.XCP, bear_credit, action='bet {}'.format(bet_match_status), event=tx['tx_hash'])
                # Pay fee to feed.
                util.credit(db, bet_match['feed_address'], config.XCP, fee, action='feed fee', event=tx['tx_hash'])
                # For logging purposes.
                bindings = {
                    'bet_match_id': bet_match_id,
                    'bet_match_type_id': bet_match_type_id,
                    'block_index': tx['block_indeM
                    'settled': True,
                    'bull_credit': bull_credit,
                    'bear_credit': bear_credit,
                    'winner': None,
                    'escrow_less_fee': None,
                    'fee': fee
                sql='insert into bet_match_resolutions values(:bet_match_id, :bet_match_type_id, :block_index, :settled, :bull_credit, :bear_credit, :winner, :escrow_less_fee, :fee)'
                cursor.execute(sql, bindings)
        # Equal[/NotEqM
        elif bet_match_type_id == equal_type_id and timestamp >= bet_match['deadline']:
            # Recognise tx0, tx1 as the bull, bear (in the right direction).
            if bet_match['tx0_bet_type'] < bet_match['tx1_bet_type']:
                equal_address = bet_match['tx0_address']
                notequal_address = bet_match['tx1_address']
                equal_address = bet_match['tx1_address']
                notequal_address = bet_match['tx0_address']
            # Decide M
who won, and credit appropriately.
            if value == bet_match['target_value']:
                winner = 'Equal'
                bet_match_status = 'settled: for equal'
                util.credit(db, equal_address, config.XCP, escrow_less_fee, action='bet {}'.format(bet_match_status), event=tx['tx_hash'])
                winner = 'NotEqual'
                bet_match_status = 'settled: for notequal'
                util.credit(db, notequal_address, config.XCP, escrow_less_fee, action='bet {}M
'.format(bet_match_status), event=tx['tx_hash'])
            # Pay fee to feed.
            util.credit(db, bet_match['feed_address'], config.XCP, fee, action='feed fee', event=tx['tx_hash'])
            # For logging purposes.
            bindings = {
                'bet_match_id': bet_match_id,
                'bet_match_type_id': bet_match_type_id,
                'block_index': tx['block_index'],
                'settled': None,
                'bull_credit': None,
                'bear_credit': None,
          'winner': winner,
                'escrow_less_fee': escrow_less_fee,
                'fee': fee
            sql='insert into bet_match_resolutions values(:bet_match_id, :bet_match_type_id, :block_index, :settled, :bull_credit, :bear_credit, :winner, :escrow_less_fee, :fee)'
            cursor.execute(sql, bindings)
        # Update the bet match
        if bet_match_status:
            bindings = {
                'status': bet_match_status,
                'bet_match_id': utilM
.make_id(bet_match['tx0_hash'], bet_match['tx1_hash'])
            sql='update bet_matches set status = :status where id = :bet_match_id'
            cursor.execute(sql, bindings)
            log.message(db, tx['block_index'], 'update', 'bet_matches', bindings)
        broadcast_bet_match_cursor.close()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
= logging.getLogger(__name__)
from counterpartylib.lib import config
from counterpartylib.lib import exceptions
from counterpartylib.lib import util
from counterpartylib.lib import log
from counterpartylib.lib import message_type
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS btcpays(
                      tx_index INTEGER PRIMARY KEY,
                      tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                    source TEXT,
                      destination TEXT,
                      btc_amount INTEGER,
                      order_match_id TEXT,
                      status TEXT,
                      FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
                      # Disallows invalids: FOREIGN KEY (order_match_id) REFERENCES order_matches(id))
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      blockM
_index_idx ON btcpays (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON btcpays (source)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      destination_idx ON btcpays (destination)
                   ''')
def validate (db, source, order_match_id, block_index):
    order_match = None
    cursor = db.cursor()
    cursor.execute('''SELECT * FROM order_matches \
                      WM
HERE id = ?''', (order_match_id,))
    order_matches = cursor.fetchall()
    if len(order_matches) == 0:
        problems.append('no such order match %s' % order_match_id)
        return None, None, None, None, order_match, problems
    elif len(order_matches) > 1:
        assert False
        order_match = order_matches[0]
        if order_match['status'] == 'expired':
            problems.append('order match expired')
        elif order_match['status'] == 'completed':
blems.append('order match completed')
        elif order_match['status'].startswith('invalid'):
            problems.append('order match invalid')
        elif order_match['status'] != 'pending':
            raise exceptions.OrderError('unrecognised order match status')
    # Figure out to which address the BTC are being paid.
    # Check that source address is correct.
    if order_match['backward_asset'] == config.BTC:
        if source != order_match['tx1_address'] and not (block_index >= 313900 or config.TESTNM
ET or config.REGTEST):  # Protocol change.
            problems.append('incorrect source address')
        destination = order_match['tx0_address']
        btc_quantity = order_match['backward_quantity']
        escrowed_asset  = order_match['forward_asset']
        escrowed_quantity = order_match['forward_quantity']
    elif order_match['forward_asset'] == config.BTC:
        if source != order_match['tx0_address'] and not (block_index >= 313900 or config.TESTNET or config.REGTEST):  # Protocol change.
 problems.append('incorrect source address')
        destination = order_match['tx1_address']
        btc_quantity = order_match['forward_quantity']
        escrowed_asset  = order_match['backward_asset']
        escrowed_quantity = order_match['backward_quantity']
        assert False
    return destination, btc_quantity, escrowed_asset, escrowed_quantity, order_match, problems
def compose (db, source, order_match_id):
    tx0_hash, tx1_hash = util.parse_id(order_match_id)
    destination, btc_quantitM
y, escrowed_asset, escrowed_quantity, order_match, problems = validate(db, source, order_match_id, util.CURRENT_BLOCK_INDEX)
    if problems: raise exceptions.ComposeError(problems)
    # Warn if down to the wire.
    time_left = order_match['match_expire_index'] - util.CURRENT_BLOCK_INDEX
    if time_left < 4:
        logger.warning('Only {} blocks until that order match expires. The payment might not make into the blockchain in time.'.format(time_left))
    if 10 - time_left < 4:
        logger.warning('Order maM
tch has only {} confirmation(s).'.format(10 - time_left))
    tx0_hash_bytes, tx1_hash_bytes = binascii.unhexlify(bytes(tx0_hash, 'utf-8')), binascii.unhexlify(bytes(tx1_hash, 'utf-8'))
    data = message_type.pack(ID)
    data += struct.pack(FORMAT, tx0_hash_bytes, tx1_hash_bytes)
    return (source, [(destination, btc_quantity)], data)
def parse (db, tx, message):
    cursor = db.cursor()
    # Unpack message.
        if len(message) != LENGTH:
            raise exceptions.UnpackError
_bytes, tx1_hash_bytes = struct.unpack(FORMAT, message)
        tx0_hash, tx1_hash = binascii.hexlify(tx0_hash_bytes).decode('utf-8'), binascii.hexlify(tx1_hash_bytes).decode('utf-8')
        order_match_id = util.make_id(tx0_hash, tx1_hash)
        status = 'valid'
    except (exceptions.UnpackError, struct.error) as e:
        tx0_hash, tx1_hash, order_match_id = None, None, None
        status = 'invalid: could not unpack'
    if status == 'valid':
        destination, btc_quantity, escrowed_asset, escrowed_quaM
ntity, order_match, problems = validate(db, tx['source'], order_match_id, tx['block_index'])
        if problems:
            order_match = None
            status = 'invalid: ' + '; '.join(problems)
    if status == 'valid':
        # BTC must be paid all at once.
        if tx['btc_amount'] >= btc_quantity:
            # Credit source address for the currency that he bought with the bitcoins.
            util.credit(db, tx['source'], escrowed_asset, escrowed_quantity, action='btcpay', event=tx['tx_hash'])
       status = 'valid'
            # Update order match.
            bindings = {
                'status': 'completed',
                'order_match_id': order_match_id
            sql='update order_matches set status = :status where id = :order_match_id'
            cursor.execute(sql, bindings)
            log.message(db, tx['block_index'], 'update', 'order_matches', bindings)
            # Update give and get order status as filled if order_match is completed
            if util.enabled('btc_orM
                bindings = {
                    'status': 'pending',
                    'tx0_hash': tx0_hash,
                    'tx1_hash': tx1_hash
                sql='select * from order_matches where status = :status and ((tx0_hash in (:tx0_hash, :tx1_hash)) or ((tx1_hash in (:tx0_hash, :tx1_hash))))'
                cursor.execute(sql, bindings)
                order_matches = cursor.fetchall()
                if len(order_matches) == 0:
                    # mark both btc gM
et and give orders as filled when order_match is completed and give or get remaining = 0
                    bindings = {
                        'status': 'filled',
                        'tx0_hash': tx0_hash,
                        'tx1_hash': tx1_hash
                    }
                    sql='update orders set status = :status where ((tx_hash in (:tx0_hash, :tx1_hash)) and ((give_remaining = 0) or (get_remaining = 0)))'
                    cursor.execute(sql, bindings)
                else:
      # always mark btc get order as filled when order_match is completed and give or get remaining = 0
                    bindings = {
                        'status': 'filled',
                        'source': tx['destination'],
                        'tx0_hash': tx0_hash,
                        'tx1_hash': tx1_hash
                    }
                    sql='update orders set status = :status where ((tx_hash in (:tx0_hash, :tx1_hash)) and ((give_remaining = 0) or (get_remaining = 0)) and (source = :sourcM
                    cursor.execute(sql, bindings)
    # Add parsed transaction to message-type
        'tx_index': tx['tx_index'],
        'tx_hash': tx['tx_hash'],
        'block_index': tx['block_index'],
        'source': tx['source'],
        'destination': tx['destination'],
        'btc_amount': tx['btc_amount'],
        'order_match_id': order_match_id,
        'status': status,
    if "integer overflow" not in status:
        sql = 'insert into btcpays values(:M
tx_index, :tx_hash, :block_index, :source, :destination, :btc_amount, :order_match_id, :status)'
        cursor.execute(sql, bindings)
        logger.warn("Not storing [btcpay] tx [%s]: %s" % (tx['tx_hash'], status))
        logger.debug("Bindings: %s" % (json.dumps(bindings), ))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
logger = logging.getLogger(__name__)
from fractions import Fraction
from counterpartylib.lib import (config, exceptions, util)
"""Burn {} to earn {} during a special period of time.""".format(config.BTC, config.XCP)
def initialise (db):
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS burns(
                      tx_index INTEGER PRIMARY KEY,
                      tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      source TEXT,
                      burned INTEGER,
           earned INTEGER,
                      status TEXT,
                      FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      status_idx ON burns (status)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      address_idx ON burns (source)
                   ''')
def validate (db, source, destination, quantity, block_indexM
    # Check destination address.
    if destination != config.UNSPENDABLE:
        problems.append('wrong destination address')
    if not isinstance(quantity, int):
        problems.append('quantity must be in satoshis')
        return problems
    if quantity < 0: problems.append('negative quantity')
    # Try to make sure that the burned funds won't go to waste.
    if block_index < config.BURN_START - 1:
        problems.append('too early')
    elif block_index > config.M
        problems.append('too late')
def compose (db, source, quantity, overburn=False):
    cursor = db.cursor()
    destination = config.UNSPENDABLE
    problems = validate(db, source, destination, quantity, util.CURRENT_BLOCK_INDEX, overburn=overburn)
    if problems: raise exceptions.ComposeError(problems)
    # Check that a maximum of 1 BTC total is burned per address.
    burns = list(cursor.execute('''SELECT * FROM burns WHERE (status = ? AND source = ?)''', ('valid', source))M
    already_burned = sum([burn['burned'] for burn in burns])
    if quantity > (1 * config.UNIT - already_burned) and not overburn:
        raise exceptions.ComposeError('1 {} may be burned per address'.format(config.BTC))
    return (source, [(destination, quantity)], None)
def parse (db, tx, MAINNET_BURNS, message=None):
    burn_parse_cursor = db.cursor()
    if config.TESTNET or config.REGTEST:
        problems = []
        status = 'valid'
        if status == 'valid':
blems = validate(db, tx['source'], tx['destination'], tx['btc_amount'], tx['block_index'], overburn=False)
            if problems: status = 'invalid: ' + '; '.join(problems)
            if tx['btc_amount'] != None:
                sent = tx['btc_amount']
                sent = 0
        if status == 'valid':
            # Calculate quantity of XCP earned. (Maximum 1 BTC in total, ever.)
            cursor = db.cursor()
            cursor.execute('''SELECT * FROM burns WHERE (status = ? AND sourM
ce = ?)''', ('valid', tx['source']))
            burns = cursor.fetchall()
            already_burned = sum([burn['burned'] for burn in burns])
            ONE = 1 * config.UNIT
            max_burn = ONE - already_burned
            if sent > max_burn: burned = max_burn   # Exceeded maximum burn; earn what you can.
            else: burned = sent
            total_time = config.BURN_END - config.BURN_START
            partial_time = config.BURN_END - tx['block_index']
            multiplier = (1000 + (500 * FractM
ion(partial_time, total_time)))
            earned = round(burned * multiplier)
            # Credit source address with earned XCP.
            util.credit(db, tx['source'], config.XCP, earned, action='burn', event=tx['tx_hash'])
            burned = 0
            earned = 0
        tx_index = tx['tx_index']
        tx_hash = tx['tx_hash']
        block_index = tx['block_index']
        source = tx['source']
        # Mainnet burns are hard
            line = MAINNM
ET_BURNS[tx['tx_hash']]
        except KeyError:
        util.credit(db, line['source'], config.XCP, int(line['earned']), action='burn', event=line['tx_hash'])
        tx_index = tx['tx_index']
        tx_hash = line['tx_hash']
        block_index = line['block_index']
        source = line['source']
        burned = line['burned']
        earned = line['earned']
        status = 'valid'
    # Add parsed transaction to message-type
    # TODO: store sent in table
        'tx_index': tx_index,
        'tx_hash': tx_hash,
        'block_index': block_index,
        'source': source,
        'burned': burned,
        'earned': earned,
        'status': status,
    if "integer overflow" not in status:
        sql = 'insert into burns values(:tx_index, :tx_hash, :block_index, :source, :burned, :earned, :status)'
        burn_parse_cursor.execute(sql, bindings)
        logger.warn("Not storing [burn] tx [%s]: %s" % (tx['tx_hash'], status))
        logger.debuM
g("Bindings: %s" % (json.dumps(bindings), ))
    burn_parse_cursor.close()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
offer_hash is the hash of either a bet or an order.
logger = logging.getLogger(__name__)
from counterpartylib.lib import (config, exceptions, util, message_type)
from . import (order, bet, rps)
def initialise (db):
    cursor = db.cursorM
    cursor.execute('''CREATE TABLE IF NOT EXISTS cancels(
                      tx_index INTEGER PRIMARY KEY,
                      tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      source TEXT,
                      offer_hash TEXT,
                      status TEXT,
                      FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
                      # Offer hash is not a foreign key. (And itM
 cannot be, because of some invalid cancels.)
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      cancels_block_index_idx ON cancels (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON cancels (source)
                   ''')
def validate (db, source, offer_hash):
    # TODO: make query only if necessary
    cursor = db.cursor()
    cursor.execute('''SELECT * from orders WHERE tx_hash = ?''', (offer_hash,))M
    orders = list(cursor)
    cursor.execute('''SELECT * from bets WHERE tx_hash = ?''', (offer_hash,))
    bets = list(cursor)
    cursor.execute('''SELECT * from rps WHERE tx_hash = ?''', (offer_hash,))
    rps = list(cursor)
    offer_type = None
    if orders: offer_type = 'order'
    elif bets: offer_type = 'bet'
    elif rps: offer_type = 'rps'
    else: problems = ['no open offer with that hash']
        offers = orders + bets + rps
        if offer['source'] != source:
            problems.append('incorrect source address')
        if offer['status'] != 'open':
            problems.append('offer not open')
    return offer, offer_type, problems
def compose (db, source, offer_hash):
    # Check that offer exists.
    offer, offer_type, problems = validate(db, source, offer_hash)
    if problems: raise exceptions.ComposeError(problems)
    offer_hash_bytes = binascii.unhexlify(bytes(offer_hash, 'utf-8'))
    data = message_type.packM
    data += struct.pack(FORMAT, offer_hash_bytes)
    return (source, [], data)
def parse (db, tx, message):
    cursor = db.cursor()
    # Unpack message.
        if len(message) != LENGTH:
            raise exceptions.UnpackError
        offer_hash_bytes = struct.unpack(FORMAT, message)[0]
        offer_hash = binascii.hexlify(offer_hash_bytes).decode('utf-8')
        status = 'valid'
    except (exceptions.UnpackError, struct.error) as e:
        offer_hash = None
        status = 'invalid: couldM
    if status == 'valid':
        offer, offer_type, problems = validate(db, tx['source'], offer_hash)
        if problems:
            status = 'invalid: ' + '; '.join(problems)
    if status == 'valid':
        # Cancel if order.
        if offer_type == 'order':
            order.cancel_order(db, offer, 'cancelled', tx['block_index'])
        # Cancel if bet.
        elif offer_type == 'bet':
            bet.cancel_bet(db, offer, 'cancelled', tx['block_index'])
        # Cancel if rps.
f offer_type == 'rps':
            rps.cancel_rps(db, offer, 'cancelled', tx['block_index'])
        # If neither order or bet, mark as invalid.
            assert False
    # Add parsed transaction to message-type
        'tx_index': tx['tx_index'],
        'tx_hash': tx['tx_hash'],
        'block_index': tx['block_index'],
        'source': tx['source'],
        'offer_hash': offer_hash,
        'status': status,
    if "integer overflow" not in status:
    sql='INSERT INTO cancels VALUES (:tx_index, :tx_hash, :block_index, :source, :offer_hash, :status)'
        cursor.execute(sql, bindings)
        logger.warn("Not storing [cancel] tx [%s]: %s" % (tx['tx_hash'], status))
        logger.debug("Bindings: %s" % (json.dumps(bindings), ))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
"""Destroy a quantity of an asset."""
logger = logging.geM
from counterpartylib.lib import util
from counterpartylib.lib import config
from counterpartylib.lib import script
from counterpartylib.lib import message_type
from counterpartylib.lib.script import AddressError
from counterpartylib.lib.exceptions import *
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS destructions(
                      tx_index INTEGER PRIMARY KEY,
                      M
tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      source TEXT,
                      asset INTEGER,
                      quantity INTEGER,
                      tag TEXT,
                      status TEXT,
                      FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      status_idx ON destructions (status)
                   '''M
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      address_idx ON destructions (source)
                   ''')
def pack(db, asset, quantity, tag):
    data = message_type.pack(ID)
    if isinstance(tag, str):
        tag = bytes(tag.encode('utf8'))[0:MAX_TAG_LENGTH]
    elif isinstance(tag, bytes):
        tag = tag[0:MAX_TAG_LENGTH]
    data += struct.pack(FORMAT, util.get_asset_id(db, asset, util.CURRENT_BLOCK_INDEX), quantity)
def unpack(db, message):
        asset_id, quantity = struct.unpack(FORMAT, message[0:16])
        tag = message[16:]
        asset = util.get_asset_name(db, asset_id, util.CURRENT_BLOCK_INDEX)
    except struct.error:
        raise UnpackError('could not unpack')
    except AssetIDError:
        raise UnpackError('asset id invalid')
    return asset, quantity, tag
def validate (db, source, destination, asset, quantity):
        util.get_asset_id(db, asset, util.CURRENT_BLOCK_INDEX)
        raise ValidateError('asset invalid')
        script.validate(source)
    except AddressError:
        raise ValidateError('source address invalid')
        raise ValidateError('destination exists')
    if asset == config.BTC:
        raise ValidateError('cannot destroy {}'.format(config.BTC))
    if type(quantity) != int:
        raise ValidateError('quantity not integer')
    if quantity > config.MAX_INT:
        raise ValidateError('integer overflow, quanM
    if quantity < 0:
        raise ValidateError('quantity negative')
    if util.get_balance(db, source, asset) < quantity:
        raise BalanceError('balance insufficient')
def compose (db, source, asset, quantity, tag):
    # resolve subassets
    asset = util.resolve_subasset_longname(db, asset)
    validate(db, source, None, asset, quantity)
    data = pack(db, asset, quantity, tag)
    return (source, [], data)
def parse (db, tx, message):
    status = 'valid'
    asset, quantity, tM
ag = None, None, None
        asset, quantity, tag = unpack(db, message)
        validate(db, tx['source'], tx['destination'], asset, quantity)
        util.debit(db, tx['source'], asset, quantity, 'destroy', tx['tx_hash'])
    except UnpackError as e:
        status = 'invalid: ' + ''.join(e.args)
    except (ValidateError, BalanceError) as e:
        status = 'invalid: ' + ''.join(e.args)
                'tx_index': tx['tx_index'],
                'tx_hash': tx['tx_hash'],
      'block_index': tx['block_index'],
                'source': tx['source'],
                'asset': asset,
                'quantity': quantity,
                'tag': tag,
                'status': status,
    if "integer overflow" not in status:
        sql = 'insert into destructions values(:tx_index, :tx_hash, :block_index, :source, :asset, :quantity, :tag, :status)'
        cursor = db.cursor()
        cursor.execute(sql, bindings)
        logger.warn("Not storing [destroy] tx [M
%s]: %s" % (tx['tx_hash'], status))
        logger.debug("Bindings: %s" % (json.dumps(bindings), ))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
# What is a dispenser?
# A dispenser is a type of order where the holder address gives out a given amount
# of units of an asset for a given amount of BTC satoshis received.
# It's a very simple but powerful semantic to allow swaps to operate on-chain.
from math import floor
logger = logging.getLogger(__name__)
from counterpartylib.lib import config
from counterpartylib.lib import exceptions
from counterpartylib.lib import util
from counterpartylib.lib import log
from counterpartylib.lib import message_type
from counterpartylib.lib import address
STATUS_OPEN_EMPTY_ADDRESS = 1
#STATUS_OPEN_ORACLE_PRICE = 20
#STATUS_OPEN_ORACLE_PRICE_EMPTY_ADDRESS = 21
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS dispensers(
                      tx_index INTEGER PRIMARY KEY,
                      tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      source TEXT,
                      asset TEXT,
                      give_quantity INTEGER,
                      escrow_quantity INTEGER,
                      satoshirate INTEGER,
                      status INTEGER,
                      give_remaining IM
                      FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
                      # Disallows invalids: FOREIGN KEY (order_match_id) REFERENCES order_matches(id))
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      dispensers_source_idx ON dispensers (source)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      dispensers_asset_idx ON dispensers (asset)
    cursor.execute('''CREATE TABLE IF NOT EXISTS dispenses(
                      tx_index INTEGER,
                      dispense_index INTEGER,
                      tx_hash TEXT,
                      block_index INTEGER,
                      source TEXT,
                      destination TEXT,
                      asset TEXT,
                      dispense_quantity INTEGER,
                      dispenser_tx_hash TEXT,
                      PRIMARY KEY (tx_index, dispense_index, source, destM
                      FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
    columns = [column['name'] for column in cursor.execute('''PRAGMA table_info(dispenses)''')]
    if 'dispenser_tx_hash' not in columns:
        cursor.execute('ALTER TABLE dispenses ADD COLUMN dispenser_tx_hash TEXT')
    columns = [column['name'] for column in cursor.execute('''PRAGMA table_info(dispensers)''')]
    if 'oracle_addrM
ess' not in columns:
        cursor.execute('ALTER TABLE dispensers ADD COLUMN oracle_address TEXT')
def validate (db, source, asset, give_quantity, escrow_quantity, mainchainrate, status, open_address, block_index, oracle_address):
    order_match = None
    if asset == config.BTC:
        problems.append('cannot dispense %s' % config.BTC)
        return None, problems
    # resolve subassets
    asset = util.resolve_subasset_longname(db, asset)
    if status == STATUS_OPEM
N or status == STATUS_OPEN_EMPTY_ADDRESS:
        if give_quantity <= 0:
            problems.append('give_quantity must be positive')
        if mainchainrate <= 0:
            problems.append('mainchainrate must be positive')
        if escrow_quantity < give_quantity:
            problems.append('escrow_quantity must be greater or equal than give_quantity')
    elif not(status == STATUS_CLOSED):
        problems.append('invalid status %i' % status)
    cursor = db.cursor()
    cursor.execute('''SELECT quantity M
                      WHERE address = ? and asset = ?''', (source,asset,))
    available = cursor.fetchall()
    if len(available) == 0:
        problems.append('address doesn\'t has the asset %s' % asset)
    elif len(available) >= 1 and available[0]['quantity'] < escrow_quantity:
        problems.append('address doesn\'t has enough balance of %s (%i < %i)' % (asset, available[0]['quantity'], escrow_quantity))
        if status == STATUS_OPEN_EMPTY_ADDRESS and not(open_address):
    open_address = source
            status = STATUS_OPEN
        query_address = open_address if status == STATUS_OPEN_EMPTY_ADDRESS else source
        cursor.execute('''SELECT * FROM dispensers WHERE source = ? AND asset = ? AND status=?''', (query_address, asset, STATUS_OPEN))
        open_dispensers = cursor.fetchall()
        if status == STATUS_OPEN or status == STATUS_OPEN_EMPTY_ADDRESS:
            if len(open_dispensers) > 0 and open_dispensers[0]['satoshirate'] != mainchainrate:
                problemM
s.append('address has a dispenser already opened for asset %s with a different mainchainrate' % asset)
            if len(open_dispensers) > 0 and open_dispensers[0]['give_quantity'] != give_quantity:
                problems.append('address has a dispenser already opened for asset %s with a different give_quantity' % asset)
        elif status == STATUS_CLOSED:
            if len(open_dispensers) == 0:
                problems.append('address doesnt has an open dispenser for asset %s' % asset)
== STATUS_OPEN_EMPTY_ADDRESS:
            cursor.execute('''SELECT count(*) cnt FROM balances WHERE address = ?''', (query_address,))
            existing_balances = cursor.fetchall()
            if existing_balances[0]['cnt'] > 0:
                problems.append('cannot open on another address if it has any balance history')
        if len(problems) == 0:
            asset_id = util.generate_asset_id(asset, block_index)
            if asset_id == 0:
                problems.append('cannot dispense %s' % asset) # M
How can we test this on a test vector?
    if oracle_address is not None and util.enabled('oracle_dispensers', block_index):
        last_price, last_fee, last_label, last_updated = util.get_oracle_last_price(db, oracle_address, block_index)
        if last_price is None:
            problems.append('The oracle address %s has not broadcasted any price yet' % oracle_address)
    if len(problems) > 0:
        return None, problems
        return asset_id, None
pose (db, source, asset, give_quantity, escrow_quantity, mainchainrate, status, open_address=None, oracle_address=None):
    assetid, problems = validate(db, source, asset, give_quantity, escrow_quantity, mainchainrate, status, open_address, util.CURRENT_BLOCK_INDEX, oracle_address)
    if problems: raise exceptions.ComposeError(problems)
    destination = []
    data = message_type.pack(ID)
    data += struct.pack(FORMAT, assetid, give_quantity, escrow_quantity, mainchainrate, status)
    if status == STATUS_OPENM
_EMPTY_ADDRESS and open_address:
        data += address.pack(open_address)
    if oracle_address is not None and util.enabled('oracle_dispensers'):
        oracle_fee = calculate_oracle_fee(db, escrow_quantity, give_quantity, mainchainrate, oracle_address, util.CURRENT_BLOCK_INDEX)
        if oracle_fee >= config.DEFAULT_REGULAR_DUST_SIZE:
            destination.append((oracle_address,oracle_fee))
        data += address.pack(oracle_address)
    return (source, destination, data)
lculate_oracle_fee(db, escrow_quantity, give_quantity, mainchainrate, oracle_address, block_index):
    last_price, last_fee, last_fiat_label, last_updated = util.get_oracle_last_price(db, oracle_address, block_index)
    last_fee_multiplier = (last_fee / config.UNIT)
    #Format mainchainrate to ######.##
    oracle_mainchainrate = util.satoshirate_to_fiat(mainchainrate)
    oracle_mainchainrate_btc = oracle_mainchainrate/last_price
    #Calculate the total amount earned for dispenser and M
    remaining = int(floor(escrow_quantity / give_quantity))
    total_quantity_btc = oracle_mainchainrate_btc * remaining
    oracle_fee_btc = int(total_quantity_btc * last_fee_multiplier *config.UNIT)
    return oracle_fee_btc
def parse (db, tx, message):
    cursor = db.cursor()
    # Unpack message.
        action_address = tx['source']
        oracle_address = None
        assetid, give_quantity, escrow_quantity, mainchainrate, dispenser_status = struct.unpack(FORMAT, message[0:LENGTH])
        read = LENGTH
        if dispenser_status == STATUS_OPEN_EMPTY_ADDRESS:
            action_address = address.unpack(message[LENGTH:LENGTH+21])
            read = LENGTH + 21
        if len(message) > read:
            oracle_address = address.unpack(message[read:read+21])
        asset = util.generate_asset_name(assetid, util.CURRENT_BLOCK_INDEX)
        status = 'valid'
    except (exceptions.UnpackError, struct.error) as e:
        assetid, give_quantity, mainchainrate, asset = None, None, None, None
    status = 'invalid: could not unpack'
    if status == 'valid':
        if util.enabled("dispenser_parsing_validation", util.CURRENT_BLOCK_INDEX):
            asset_id, problems = validate(db, tx['source'], asset, give_quantity, escrow_quantity, mainchainrate, dispenser_status, action_address if dispenser_status == STATUS_OPEN_EMPTY_ADDRESS else None, tx['block_index'], oracle_address)
            problems = None
        if problems:
            status = 'invalid: ' + '; '.join(problM
            if dispenser_status == STATUS_OPEN or dispenser_status == STATUS_OPEN_EMPTY_ADDRESS:
                cursor.execute('SELECT * FROM dispensers WHERE source=:source AND asset=:asset AND status=:status', {
                    'source': action_address,
                    'asset': asset,
                    'status': STATUS_OPEN
                existing = cursor.fetchall()
                if len(existing) == 0:
                    if (oracle_address != None) and utiM
l.enabled('oracle_dispensers', tx['block_index']):
                        oracle_fee = calculate_oracle_fee(db, escrow_quantity, give_quantity, mainchainrate, oracle_address, tx['block_index'])

                        if oracle_fee >= config.DEFAULT_REGULAR_DUST_SIZE:
                            if tx["destination"] != oracle_address or tx["btc_amount"] < oracle_fee:
                                status = 'invalid: insufficient or non-existent oracle fee'


                    if status == 'valid':
                        # Create the new dispenser
                        try:
                            if dispenser_status == STATUS_OPEN_EMPTY_ADDRESS:
                                cursor.execute('SELECT count(*) cnt FROM balances WHERE address=:address AND quantity > 0', {
                                    'address': action_address
                                })
                                counts = cursor.fetchall()[0]
                   if counts['cnt'] == 0:
                                    util.debit(db, tx['source'], asset, escrow_quantity, action='open dispenser empty addr', event=tx['tx_hash'])
                                    util.credit(db, action_address, asset, escrow_quantity, action='open dispenser empty addr', event=tx['tx_hash'])
                                    util.debit(db, action_address, asset, escrow_quantity, action='open dispenser empty addr', event=tx['tx_hash'])
                                elsM
                                    status = 'invalid: address not empty'
                            else:
                                util.debit(db, tx['source'], asset, escrow_quantity, action='open dispenser', event=tx['tx_hash'])
                        except util.DebitError as e:
                            status = 'invalid: insufficient funds'
                    if status == 'valid':
                        bindings = {
                            'tx_index': tx['tx_index'],
                      M
      'tx_hash': tx['tx_hash'],
                            'block_index': tx['block_index'],
                            'source': action_address,
                            'asset': asset,
                            'give_quantity': give_quantity,
                            'escrow_quantity': escrow_quantity,
                            'satoshirate': mainchainrate,
                            'status': STATUS_OPEN,
                            'give_remaining': escrow_quantity,
                            'oraM
cle_address': oracle_address
                        }
                        sql = 'insert into dispensers values(:tx_index, :tx_hash, :block_index, :source, :asset, :give_quantity, :escrow_quantity, :satoshirate, :status, :give_remaining, :oracle_address)'
                        cursor.execute(sql, bindings)
                elif len(existing) == 1 and existing[0]['satoshirate'] == mainchainrate and existing[0]['give_quantity'] == give_quantity:
                    if tx["source"]==action_address:
          if (oracle_address != None) and util.enabled('oracle_dispensers', tx['block_index']):
                            oracle_fee = calculate_oracle_fee(db, escrow_quantity, give_quantity, mainchainrate, oracle_address, tx['block_index'])

                            if oracle_fee >= config.DEFAULT_REGULAR_DUST_SIZE:
                                if tx["destination"] != oracle_address or tx["btc_amount"] < oracle_fee:
                                    status = 'invalid: iM
nsufficient or non-existent oracle fee'

                        if status == 'valid':
                            # Refill the dispenser by the given amount
                            bindings = {
                                'source': tx['source'],
                                'asset': asset,
                                'prev_status': dispenser_status,
                                'give_remaining': existing[0]['give_remaining'] + escrow_quantity,
                             M
   'status': STATUS_OPEN,
                                'block_index': tx['block_index'],
                                'action':'refill dispenser',
                                'escrow_quantity':escrow_quantity
                            }
                            try:
                                util.debit(db, tx['source'], asset, escrow_quantity, action='refill dispenser', event=tx['tx_hash'])
                                sql = 'UPDATE dispensers SET give_remaining=:give_remaining \
                         WHERE source=:source AND asset=:asset AND status=:status'
                                cursor.execute(sql, bindings)
                            except (util.DebitError):
                                status = 'insufficient funds'
                    else:
                        status = 'invalid: can only refill dispenser from source'
                else:
                    status = 'can only have one open dispenser per asset per address'
if dispenser_status == STATUS_CLOSED:
                cursor.execute('SELECT tx_index, give_remaining FROM dispensers WHERE source=:source AND asset=:asset AND status=:status', {
                    'source': tx['source'],
                    'asset': asset,
                    'status': STATUS_OPEN
                existing = cursor.fetchall()
                if len(existing) == 1:
                    util.credit(db, tx['source'], asset, existing[0]['give_remaining'], action='close dispenser', eM
                    bindings = {
                        'source': tx['source'],
                        'asset': asset,
                        'status': STATUS_CLOSED,
                        'block_index': tx['block_index'],
                        'tx_index': existing[0]['tx_index']
                    }
                    sql = 'UPDATE dispensers SET give_remaining=0, status=:status WHERE source=:source AND asset=:asset'
                    cursor.execute(sql, bindings)
                elsM
                    status = 'dispenser inexistent'
                status = 'invalid: status must be one of OPEN or CLOSE'
    if status != 'valid':
        logger.warn("Not storing [dispenser] tx [%s]: %s" % (tx['tx_hash'], status))
def is_dispensable(db, address, amount):
    cursor = db.cursor()
    cursor.execute('SELECT * FROM dispensers WHERE source=:source AND status=:status', {
        'source': address,
        'status': STATUS_OPEN
    dispensers = cuM
    for next_dispenser in dispensers:
        if next_dispenser["oracle_address"] != None:
            last_price, last_fee, last_fiat_label, last_updated = util.get_oracle_last_price(db, next_dispenser['oracle_address'], util.CURRENT_BLOCK_INDEX)
            fiatrate = util.satoshirate_to_fiat(next_dispenser["satoshirate"])
            if amount >= fiatrate/last_price:
                return True
            if amount >= next_dispenser["satoshirate"]:
def dispense(db, tx):
    cursor = db.cursor()
    cursor.execute('SELECT * FROM dispensers WHERE source=:source AND status=:status ORDER BY asset', {
        'source': tx['destination'],
        'status': STATUS_OPEN
    dispensers = cursor.fetchall()
    dispense_index = 0
    for dispenser in dispensers:
        satoshirate = dispenser['satoshirate']
        give_quantity = dispenser['give_quantity']
        if satoshirate > 0 and give_quantity > 0:
if (dispenser['oracle_address'] != None) and util.enabled('oracle_dispensers', tx['block_index']):
                last_price, last_fee, last_fiat_label, last_updated = util.get_oracle_last_price(db, dispenser['oracle_address'], tx['block_index'])
                fiatrate = util.satoshirate_to_fiat(satoshirate)
                must_give = int(floor(((tx['btc_amount'] / config.UNIT) * last_price)/fiatrate))
                must_give = int(floor(tx['btc_amount'] / satoshirate))
        remaining = int(floor(dispenser['give_remaining'] / give_quantity))
            actually_given = min(must_give, remaining) * give_quantity
            give_remaining = dispenser['give_remaining'] - actually_given
            assert give_remaining >= 0
            # Skip dispense if quantity is 0
            if util.enabled('zero_quantity_value_adjustment_1') and actually_given==0:
                continue
            util.credit(db, tx['source'], dispenser['asset'], actually_given, action='dispense', eveM
            dispenser['give_remaining'] = give_remaining
            if give_remaining < dispenser['give_quantity']:
                # close the dispenser
                dispenser['give_remaining'] = 0
                if give_remaining > 0:
                    # return the remaining to the owner
                    util.credit(db, dispenser['source'], dispenser['asset'], give_remaining, action='dispenser close', event=tx['tx_hash'])
                dispenser['status'] = STATUS_CLOSED
  dispenser['block_index'] = tx['block_index']
            dispenser['prev_status'] = STATUS_OPEN
            cursor.execute('UPDATE DISPENSERS SET give_remaining=:give_remaining, status=:status \
                    WHERE source=:source AND asset=:asset AND satoshirate=:satoshirate AND give_quantity=:give_quantity AND status=:prev_status', dispenser)
            bindings = {
                'tx_index': tx['tx_index'],
                'tx_hash': tx['tx_hash'],
                'dispense_index': dispense_index,
            'block_index': tx['block_index'],
                'source': tx['destination'],
                'destination': tx['source'],
                'asset': dispenser['asset'],
                'dispense_quantity': actually_given,
                'dispenser_tx_hash': dispenser['tx_hash']
            sql = 'INSERT INTO dispenses(tx_index, dispense_index, tx_hash, block_index, source, destination, asset, dispense_quantity, dispenser_tx_hash) \
                    VALUES(:tx_index, :dispense_index, :tM
x_hash, :block_index, :source, :destination, :asset, :dispense_quantity, :dispenser_tx_hash);'
            cursor.execute(sql, bindings)
            dispense_index += 1
"""Pay out dividends."""
logger = logging.getLogger(__name__)
from counterpartylib.lib import (config, exceptions, util, message_type)
def initialise (db):
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS dividends(
                      tx_index INTEGER PRIMARY KEY,
                      tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      source TEXT,
                      asset TEXT,
                      dividend_asset TEXT,
                      quantity_per_unit INTEGER,
                      fee_paid INTEGER,
                      status TEXT,
                      FOREIM
GN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON dividends (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON dividends (source)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      asset_idx ON dividends (asset)
                   ''')
lidate (db, source, quantity_per_unit, asset, dividend_asset, block_index):
    cursor = db.cursor()
    if asset == config.BTC:
        problems.append('cannot pay dividends to holders of {}'.format(config.BTC))
    if asset == config.XCP:
        if (not block_index >= 317500) or block_index >= 320000 or config.TESTNET or config.REGTEST:   # Protocol change.
            problems.append('cannot pay dividends to holders of {}'.format(config.XCP))
    if quantity_per_unit <= 0:
positive quantity per unit')
    if quantity_per_unit > config.MAX_INT:
        problems.append('integer overflow')
    # Examine asset.
    issuances = list(cursor.execute('''SELECT * FROM issuances WHERE (status = ? AND asset = ?) ORDER BY tx_index ASC''', ('valid', asset)))
    if not issuances:
        problems.append('no such asset, {}.'.format(asset))
        return None, None, problems, 0
    divisible = issuances[0]['divisible']
    # Only issuer can pay dividends.
lock_index >= 320000 or config.TESTNET or config.REGTEST:   # Protocol change.
        if issuances[-1]['issuer'] != source:
            problems.append('only issuer can pay dividends')
    # Examine dividend asset.
    if dividend_asset in (config.BTC, config.XCP):
        dividend_divisible = True
        issuances = list(cursor.execute('''SELECT * FROM issuances WHERE (status = ? AND asset = ?)''', ('valid', dividend_asset)))
        if not issuances:
            problems.append('no such dividend asseM
t, {}.'.format(dividend_asset))
            return None, None, problems, 0
        dividend_divisible = issuances[0]['divisible']
    # Calculate dividend quantities.
    exclude_empty = False
    if util.enabled('zero_quantity_value_adjustment_1'):
        exclude_empty = True
    holders = util.holders(db, asset, exclude_empty)
    dividend_total = 0
    for holder in holders:
        if block_index < 294500 and not (config.TESTNET or config.REGTEST): # Protocol change.
       if holder['escrow']: continue
        address = holder['address']
        address_quantity = holder['address_quantity']
        if block_index >= 296000 or config.TESTNET or config.REGTEST: # Protocol change.
            if address == source: continue
        dividend_quantity = address_quantity * quantity_per_unit
        if divisible: dividend_quantity /= config.UNIT
        if not util.enabled('nondivisible_dividend_fix') and not dividend_divisible: dividend_quantity /= config.UNIT # Pre-fix behaviourM
        if dividend_asset == config.BTC and dividend_quantity < config.DEFAULT_MULTISIG_DUST_SIZE: continue    # A bit hackish.
        dividend_quantity = int(dividend_quantity)
        outputs.append({'address': address, 'address_quantity': address_quantity, 'dividend_quantity': dividend_quantity})
        addresses.append(address)
        dividend_total += dividend_quantity
    if not dividend_total: problems.append('zero dividend')
    if dividend_asset != config.BTC:
        dividend_balances = list(cursoM
r.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, dividend_asset)))
        if not dividend_balances or dividend_balances[0]['quantity'] < dividend_total:
            problems.append('insufficient funds ({})'.format(dividend_asset))
    if not problems and dividend_asset != config.BTC:
        holder_count = len(set(addresses))
        if block_index >= 330000 or config.TESTNET or config.REGTEST: # Protocol change.
            fee = int(0.0002 * config.UNIT * holder_couM
            balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, config.XCP)))
            if not balances or balances[0]['quantity'] < fee:
                problems.append('insufficient funds ({})'.format(config.XCP))
    if not problems and dividend_asset == config.XCP:
        total_cost = dividend_total + fee
        if not dividend_balances or dividend_balances[0]['quantity'] < total_cost:
            problems.append('insufficient funds ({M
})'.format(dividend_asset))
    if fee > config.MAX_INT or dividend_total > config.MAX_INT:
        problems.append('integer overflow')
    return dividend_total, outputs, problems, fee
def compose (db, source, quantity_per_unit, asset, dividend_asset):
    # resolve subassets
    asset = util.resolve_subasset_longname(db, asset)
    dividend_asset = util.resolve_subasset_longname(db, dividend_asset)
    dividend_total, outputs, problems, fee = validate(db, source, quantityM
_per_unit, asset, dividend_asset, util.CURRENT_BLOCK_INDEX)
    if problems: raise exceptions.ComposeError(problems)
    logger.info('Total quantity to be distributed in dividends: {} {}'.format(util.value_out(db, dividend_total, dividend_asset), dividend_asset))
    if dividend_asset == config.BTC:
        return (source, [(output['address'], output['dividend_quantity']) for output in outputs], None)
    asset_id = util.get_asset_id(db, asset, util.CURRENT_BLOCK_INDEX)
    dividend_asset_id = util.get_asset_id(dM
b, dividend_asset, util.CURRENT_BLOCK_INDEX)
    data = message_type.pack(ID)
    data += struct.pack(FORMAT_2, quantity_per_unit, asset_id, dividend_asset_id)
    return (source, [], data)
def parse (db, tx, message):
    dividend_parse_cursor = db.cursor()
    # Unpack message.
        if (tx['block_index'] > 288150 or config.TESTNET or config.REGTEST) and len(message) == LENGTH_2:
            quantity_per_unit, asset_id, dividend_asset_id = struct.unpack(FORMAT_2, message)
            asset = util.getM
_asset_name(db, asset_id, tx['block_index'])
            dividend_asset = util.get_asset_name(db, dividend_asset_id, tx['block_index'])
            status = 'valid'
        elif len(message) == LENGTH_1:
            quantity_per_unit, asset_id = struct.unpack(FORMAT_1, message)
            asset = util.get_asset_name(db, asset_id, tx['block_index'])
            dividend_asset = config.XCP
            status = 'valid'
            raise exceptions.UnpackError
    except (exceptions.UnpackError, exceptioM
ns.AssetNameError, struct.error) as e:
        dividend_asset, quantity_per_unit, asset = None, None, None
        status = 'invalid: could not unpack'
    if dividend_asset == config.BTC:
        status = 'invalid: cannot pay {} dividends within protocol'.format(config.BTC)
    if status == 'valid':
        # For SQLite3
        quantity_per_unit = min(quantity_per_unit, config.MAX_INT)
        dividend_total, outputs, problems, fee = validate(db, tx['source'], quantity_per_unit, asset, dividend_asset, block_inM
dex=tx['block_index'])
        if problems: status = 'invalid: ' + '; '.join(problems)
    if status == 'valid':
        util.debit(db, tx['source'], dividend_asset, dividend_total, action='dividend', event=tx['tx_hash'])
        if tx['block_index'] >= 330000 or config.TESTNET or config.REGTEST: # Protocol change.
            util.debit(db, tx['source'], config.XCP, fee, action='dividend fee', event=tx['tx_hash'])
        for output in outputs:
            if not util.enabled('M
dont_credit_zero_dividend') or output['dividend_quantity'] > 0:
                util.credit(db, output['address'], dividend_asset, output['dividend_quantity'], action='dividend', event=tx['tx_hash'])
    # Add parsed transaction to message-type
        'tx_index': tx['tx_index'],
        'tx_hash': tx['tx_hash'],
        'block_index': tx['block_index'],
        'source': tx['source'],
        'asset': asset,
        'dividend_asset': dividend_asset,
        'quantity_per_unit': M
        'fee_paid': fee,
        'status': status,
    if "integer overflow" not in status:
        sql = 'insert into dividends values(:tx_index, :tx_hash, :block_index, :source, :asset, :dividend_asset, :quantity_per_unit, :fee_paid, :status)'
        dividend_parse_cursor.execute(sql, bindings)
        logger.warn("Not storing [dividend] tx [%s]: %s" % (tx['tx_hash'], status))
        logger.debug("Bindings: %s" % (json.dumps(bindings), ))
    dividend_parse_cursor.close()
 vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
Allow simultaneous lock and transfer.
logger = logging.getLogger(__name__)
from counterpartylib.lib import (config, util, exceptions, util, message_type)
LENGTH_1 = 8 + 8 + 1
FORMAT_2 = '>QQ??If'
LENGTH_2 = 8 + 8 + 1 + 1 + 4 + 4
SUBASSET_FORMAT = '>QQ?B'
SUBASSET_FORMAT_LENGTH = 8 + 8 + 1 + 1
# NOTE: Pascal strings are used for storing descriptions for backwards
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS issuances(
                      tx_index INTEGER PRIMARY KEY,
                      tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      asset TEXT,
                      quantity INTEGER,
                      divisible BOOL,
                      source TEXT,
                      issuer TEXT,
                      transfer BOOL,
                      callable BOOL,
                      call_date INTEGER,
                      call_price REAL,
                      description TEXT,
                      fee_paid INTEGER,
                      locked BOOL,
                      status TEXT,
                      asset_longname TEXT,
                      reset BOOL,
                      FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
    # Add asset_longname for sub-assets
t do `ALTER TABLE IF COLUMN NOT EXISTS`.
    columns = [column['name'] for column in cursor.execute('''PRAGMA table_info(issuances)''')]
    if 'asset_longname' not in columns:
        cursor.execute('''ALTER TABLE issuances ADD COLUMN asset_longname TEXT''')
    if 'reset' not in columns:
        cursor.execute('''ALTER TABLE issuances ADD COLUMN reset BOOL''')
    # If sweep_hotfix activated, Create issuances copy, copy old data, drop old tabM
le, rename new table, recreate indexes
t do `ALTER TABLE IF COLUMN NOT EXISTS` nor can drop UNIQUE constraints
    if 'msg_index' not in columns:
            cursor.execute('''CREATE TABLE IF NOT EXISTS new_issuances(
                              tx_index INTEGER,
                              tx_hash TEXT,
                              msg_index INTEGER DEFAULT 0,
                              block_index INTEGER,
                              asset TEXT,
                              quantitM
                              divisible BOOL,
                              source TEXT,
                              issuer TEXT,
                              transfer BOOL,
                              callable BOOL,
                              call_date INTEGER,
                              call_price REAL,
                              description TEXT,
                              fee_paid INTEGER,
                              locked BOOL,
                              status TEXT,
                     asset_longname TEXT,
                              reset BOOL,
                              PRIMARY KEY (tx_index, msg_index),
                              FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index),
                              UNIQUE (tx_hash, msg_index))
                           ''')
            cursor.execute('''INSERT INTO new_issuances(tx_index, tx_hash, msg_index,
                block_index, asset, quantity, divisible, sourcM
e, issuer, transfer, callable,
                call_date, call_price, description, fee_paid, locked, status, asset_longname, reset)
                SELECT tx_index, tx_hash, 0, block_index, asset, quantity, divisible, source,
                issuer, transfer, callable, call_date, call_price, description, fee_paid,
                locked, status, asset_longname, reset FROM issuances''', {})
            cursor.execute('DROP TABLE issuances')
            cursor.execute('ALTER TABLE new_issuances RENAME TO issuances')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON issuances (block_index)
                    ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      valid_asset_idx ON issuances (asset, status)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      status_idx ON issuances (status)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON issuances (source)
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      asset_longname_idx ON issuances (asset_longname)
                   ''')
def validate (db, source, destination, asset, quantity, divisible, lock, reset, callable_, call_date, call_price, description, subasset_parent, subasset_longname, block_index):
    if asset in (config.BTC, config.XCP):
        problems.append('cannot issue {} or {}'.format(config.BTC, config.XCP))
    if call_date is None: cM
    if call_price is None: call_price = 0.0
    if description is None: description = ""
    if divisible is None: divisible = True
    if lock is None: lock = False
    if reset is None: reset = False
    if isinstance(call_price, int): call_price = float(call_price)
    #^ helps especially with calls from JS
based clients, where parseFloat(15) returns 15 (not 15.0), which json takes as an int
    if not isinstance(quantity, int):
        problems.append('quantity must be in satoshis')
urn call_date, call_price, problems, fee, description, divisible, None, None
    if call_date and not isinstance(call_date, int):
        problems.append('call_date must be epoch integer')
        return call_date, call_price, problems, fee, description, divisible, None, None
    if call_price and not isinstance(call_price, float):
        problems.append('call_price must be a float')
        return call_date, call_price, problems, fee, description, divisible, None, None
    if quantity < 0: problems.append('negatM
    if call_price < 0: problems.append('negative call price')
    if call_date < 0: problems.append('negative call date')
    # Callable, or not.
    if not callable_:
        if block_index >= 312500 or config.TESTNET or config.REGTEST: # Protocol change.
            call_date = 0
            call_price = 0.0
        elif block_index >= 310000:                 # Protocol change.
            if call_date:
                problems.append('call date for non
            if call_priceM
                problems.append('call price for non
    # Valid re-issuance?
    cursor = db.cursor()
    cursor.execute('''SELECT * FROM issuances \
                      WHERE (status = ? AND asset = ?)
                      ORDER BY tx_index ASC''', ('valid', asset))
    issuances = cursor.fetchall()
    reissued_asset_longname = None
        reissuance = True
        last_issuance = issuances[-1]
        reissued_asset_longname = last_issuance['asset_loM
        issuance_locked = False
        if util.enabled('issuance_lock_fix'):
            for issuance in issuances:
                if issuance['locked']:
                    issuance_locked = True
                    break
        elif last_issuance['locked']:
            # before the issuance_lock_fix, only the last issuance was checked
            issuance_locked = True
        if last_issuance['issuer'] != source:
            problems.append('issued by another address')
        if (bool(last_issuanceM
['divisible']) != bool(divisible)) and ((not util.enabled("cip03", block_index)) or (not reset)):
            problems.append('cannot change divisibility')
        if bool(last_issuance['callable']) != bool(callable_):
            problems.append('cannot change callability')
        if last_issuance['call_date'] > call_date and (call_date != 0 or (block_index < 312500 and (not config.TESTNET or not config.REGTEST))):
            problems.append('cannot advance call date')
        if last_issuance['call_price'] > caM
            problems.append('cannot reduce call price')
        if issuance_locked and quantity:
            problems.append('locked asset and non
        if issuance_locked and reset:
            problems.append('cannot reset a locked asset')
        reissuance = False
        if description.lower() == 'lock':
            problems.append('cannot lock a non
        #if destination:
        #    problems.append('cannot transfer a non
            problems.append('cannot reset a non existent asset')
    # validate parent ownership for subasset
    if subasset_longname is not None and not reissuance:
        cursor = db.cursor()
        cursor.execute('''SELECT * FROM issuances \
                          WHERE (status = ? AND asset = ?)
                          ORDER BY tx_index ASC''', ('valid', subasset_parent))
        parent_issuances = cursor.fetchall()
        cursor.close()
        if parent_issuances:
            last_parent_M
issuance = parent_issuances[-1]
            if last_parent_issuance['issuer'] != source:
                problems.append('parent asset owned by another address')
            problems.append('parent asset not found')
    # validate subasset issuance is not a duplicate
    if subasset_longname is not None and not reissuance:
        cursor = db.cursor()
        cursor.execute('''SELECT * FROM assets \
                          WHERE (asset_longname = ?)''', (subasset_longname,))
        assets = cursorM
        if len(assets) > 0:
            problems.append('subasset already exists')
        # validate that the actual asset is numeric
        if asset[0] != 'A':
            problems.append('a subasset must be a numeric asset')
    # Check for existence of fee funds.
    if quantity or (block_index >= 315000 or config.TESTNET or config.REGTEST):   # Protocol change.
        if not reissuance or (block_index < 310000 and not config.TESTNET and not config.REGTEST):  # Pay fee only upon first issuanceM
. (Protocol change.)
            cursor = db.cursor()
            cursor.execute('''SELECT * FROM balances \
                              WHERE (address = ? AND asset = ?)''', (source, config.XCP))
            balances = cursor.fetchall()
            cursor.close()
            if util.enabled('numeric_asset_names'):  # Protocol change.
                if subasset_longname is not None and util.enabled('subassets'): # Protocol change.
                    # subasset issuance is 0.25
                    fee = int(0.25M
                elif len(asset) >= 13:
                    fee = 0
                else:
                    fee = int(0.5 * config.UNIT)
            elif block_index >= 291700 or config.TESTNET or config.REGTEST:     # Protocol change.
                fee = int(0.5 * config.UNIT)
            elif block_index >= 286000 or config.TESTNET or config.REGTEST:   # Protocol change.
                fee = 5 * config.UNIT
            elif block_index > 281236 or config.TESTNET or config.REGTEST:    # ProtocoM
                fee = 5
            if fee and (not balances or balances[0]['quantity'] < fee):
                problems.append('insufficient funds')
    if not (block_index >= 317500 or config.TESTNET or config.REGTEST):  # Protocol change.
        if len(description) > 42:
            problems.append('description too long')
    call_date = min(call_date, config.MAX_INT)
    assert isinstance(quantity, int)
    if reset and util.enabled("cip03", block_index):#reset will overwrite the M
        if quantity > config.MAX_INT:
            problems.append('total quantity overflow')
        total = sum([issuance['quantity'] for issuance in issuances])
        if total + quantity > config.MAX_INT:
            problems.append('total quantity overflow')
    if util.enabled("cip03", block_index) and reset and issuances:
        cursor = db.cursor()
        #Checking that all supply are held by the owner of the asset
        cursor.execute('''SELECT * FROM balances \
          WHERE asset = ? AND quantity > 0''', (asset,))
        balances = cursor.fetchall()
        cursor.close()
        if (len(balances) == 0):
            if util.asset_supply(db, asset) > 0:
                problems.append('Cannot reset an asset with no holder')
        elif (len(balances) > 1):
            problems.append('Cannot reset an asset with many holders')
        elif (len(balances) == 1):
            if (balances[0]['address'] != last_issuance["issuer"]):
                problems.append(M
'Cannot reset an asset held by a different address than the owner')
    #if destination and quantity:
    #    problems.append('cannot issue and transfer simultaneously')
    if util.enabled('integer_overflow_fix', block_index=block_index) and (fee > config.MAX_INT or quantity > config.MAX_INT):
        problems.append('integer overflow')
    return call_date, call_price, problems, fee, description, divisible, lock, reset, reissuance, reissued_asset_longname
def compose (db, source, transfer_M
destination, asset, quantity, divisible, lock, reset, description):
    # Callability is deprecated, so for re
issuances set relevant parameters
    # to old values; for first issuances, make uncallable.
    cursor = db.cursor()
    cursor.execute('''SELECT * FROM issuances \
                      WHERE (status = ? AND asset = ?)
                      ORDER BY tx_index ASC''', ('valid', asset))
    issuances = cursor.fetchall()
        last_issuance = issuances[-1]
        callable_ = last_issuM
        call_date = last_issuance['call_date']
        call_price = last_issuance['call_price']
        callable_ = False
        call_date = 0
        call_price = 0.0
    # check subasset
    subasset_parent = None
    subasset_longname = None
    if util.enabled('subassets'): # Protocol change.
        subasset_parent, subasset_longname = util.parse_subasset_from_asset_name(asset)
        if subasset_longname is not None:
            # try to find an existing subassM
            sa_cursor = db.cursor()
            sa_cursor.execute('''SELECT * FROM assets \
                              WHERE (asset_longname = ?)''', (subasset_longname,))
            assets = sa_cursor.fetchall()
            sa_cursor.close()
            if len(assets) > 0:
                # this is a reissuance
                asset = assets[0]['asset_name']
                # this is a new issuance
                #   generate a random numeric asset id which will map to this subasset
           asset = util.generate_random_asset()
    call_date, call_price, problems, fee, description, divisible, lock, reset, reissuance, reissued_asset_longname = validate(db, source, transfer_destination, asset, quantity, divisible, lock, reset, callable_, call_date, call_price, description, subasset_parent, subasset_longname, util.CURRENT_BLOCK_INDEX)
    if problems: raise exceptions.ComposeError(problems)
    asset_id = util.generate_asset_id(asset, util.CURRENT_BLOCK_INDEX)
    if subasset_longname is NoneM
        asset_format = util.get_value_by_block_index("issuance_asset_serialization_format")
        asset_format_length = util.get_value_by_block_index("issuance_asset_serialization_length")
        # Type 20 standard issuance FORMAT_2 >QQ??If
        #   used for standard issuances and all reissuances
        data = message_type.pack(ID)
        if (len(description) <= 42) and not util.enabled('pascal_string_removed'):
            curr_format = FORMAT_2 + '{}p'.format(len(description) + 1)M
            curr_format = asset_format + '{}s'.format(len(description))
        if (asset_format_length <= 19):# callbacks parameters were removed
            data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if lock else 0, 1 if reset else 0, description.encode('utf-8'))
        elif (asset_format_length <= 26):
            data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if callable_ else 0,
                call_date or 0, call_priceM
 or 0.0, description.encode('utf-8'))
        elif (asset_format_length <= 27):# param reset was inserted
            data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if reset else 0, 1 if callable_ else 0,
                call_date or 0, call_price or 0.0, description.encode('utf-8'))
        elif (asset_format_length <= 28):# param lock was inserted
            data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if lock else 0, 1 if reset else 0, 1 if callablM
                call_date or 0, call_price or 0.0, description.encode('utf-8'))
        subasset_format = util.get_value_by_block_index("issuance_subasset_serialization_format",util.CURRENT_BLOCK_INDEX)
        subasset_format_length = util.get_value_by_block_index("issuance_subasset_serialization_length",util.CURRENT_BLOCK_INDEX)
        # Type 21 subasset issuance SUBASSET_FORMAT >QQ?B
        #   Used only for initial subasset issuance
        # compacts a subasset name to save space
  compacted_subasset_longname = util.compact_subasset_longname(subasset_longname)
        compacted_subasset_length = len(compacted_subasset_longname)
        data = message_type.pack(SUBASSET_ID)
        curr_format = subasset_format + '{}s'.format(compacted_subasset_length) + '{}s'.format(len(description))
        if subasset_format_length <= 18:
            data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, compacted_subasset_length, compacted_subasset_longname, description.encoM
        elif subasset_format_length <= 19:# param reset was inserted
            data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if reset else 0, compacted_subasset_length, compacted_subasset_longname, description.encode('utf-8'))
        elif subasset_format_length <= 20:# param lock was inserted
            data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if lock else 0, 1 if reset else 0, compacted_subasset_length, compacted_subasset_longnaM
me, description.encode('utf-8'))
    if transfer_destination:
        destination_outputs = [(transfer_destination, None)]
        destination_outputs = []
    return (source, destination_outputs, data)
def parse (db, tx, message, message_type_id):
    issuance_parse_cursor = db.cursor()
    asset_format = util.get_value_by_block_index("issuance_asset_serialization_format",tx['block_index'])
    asset_format_length = util.get_value_by_block_index("issuance_asset_serialization_length",tx['block_index'])
    subasset_format = util.get_value_by_block_index("issuance_subasset_serialization_format",tx['block_index'])
    subasset_format_length = util.get_value_by_block_index("issuance_subasset_serialization_length",tx['block_index'])
    # Unpack message.
        subasset_longname = None
        if message_type_id == SUBASSET_ID:
            if not util.enabled('subassets', block_index=tx['block_index']):
                logger.warn("subassets are not enabled at block %s" % tx['block_index'])
 raise exceptions.UnpackError
            # parse a subasset original issuance message
            lock = None
            reset = None
            if subasset_format_length <= 18:
                asset_id, quantity, divisible, compacted_subasset_length = struct.unpack(subasset_format, message[0:subasset_format_length])
            elif subasset_format_length <= 19:# param reset was inserted
                asset_id, quantity, divisible, reset, compacted_subasset_length = struct.unpack(subasset_formatM
, message[0:subasset_format_length])
            elif subasset_format_length <= 20:# param lock was inserted
                asset_id, quantity, divisible, lock, reset, compacted_subasset_length = struct.unpack(subasset_format, message[0:subasset_format_length])
            description_length = len(message) - subasset_format_length - compacted_subasset_length
            if description_length < 0:
                logger.warn("invalid subasset length: [issuance] tx [%s]: %s" % (tx['tx_haM
sh'], compacted_subasset_length))
                raise exceptions.UnpackError
            messages_format = '>{}s{}s'.format(compacted_subasset_length, description_length)
            compacted_subasset_longname, description = struct.unpack(messages_format, message[subasset_format_length:])
            subasset_longname = util.expand_subasset_longname(compacted_subasset_longname)
            callable_, call_date, call_price = False, 0, 0.0
                description = description.decode('utf-8')
            except UnicodeDecodeError:
                description = ''
        elif (tx['block_index'] > 283271 or config.TESTNET or config.REGTEST) and len(message) >= asset_format_length: # Protocol change.
            if (len(message) - asset_format_length <= 42) and not util.enabled('pascal_string_removed'):
                curr_format = asset_format + '{}p'.format(len(message) - asset_format_length)
                curr_format = asset_format + '{}s'.format(len(message) - asset_format_length)M
            lock = None
            reset = None
            if (asset_format_length <= 19):# callbacks parameters were removed
                asset_id, quantity, divisible, lock, reset, description = struct.unpack(curr_format, message)
                callable_, call_date, call_price = False, 0, 0.0
            elif (asset_format_length <= 26):#the reset param didn't even exist
                asset_id, quantity, divisible, callable_, call_date, call_price, description = struct.unpack(curr_format, mM
            elif (asset_format_length <= 27):# param reset was inserted
                asset_id, quantity, divisible, reset, callable_, call_date, call_price, description = struct.unpack(curr_format, message)
            elif (asset_format_length <= 28):# param lock was inserted
                asset_id, quantity, divisible, lock, reset, callable_, call_date, call_price, description = struct.unpack(curr_format, message)
            call_price = round(call_price, 6) # TODO: arbitrary
                description = description.decode('utf-8')
            except UnicodeDecodeError:
                description = ''
            if len(message) != LENGTH_1:
                raise exceptions.UnpackError
            asset_id, quantity, divisible = struct.unpack(FORMAT_1, message)
            lock, reset, callable_, call_date, call_price, description = False, False, False, 0, 0.0, ''
            asset = util.generate_asset_name(asset_id, tx['block_index'])
            ##This is for backwards compatibility with assets names longer than 12 characters
            if asset.startswith('A'):
                namedAsset = util.get_asset_name(db, asset_id, tx['block_index'])
                if (namedAsset != 0):
                    asset = namedAsset
            status = 'valid'
        except exceptions.AssetIDError:
            asset = None
            status = 'invalid: bad asset name'
    except exceptions.UnpackError as e:
et, quantity, divisible, lock, reset, callable_, call_date, call_price, description = None, None, None, None, None, None, None, None, None
        status = 'invalid: could not unpack'
    # parse and validate the subasset from the message
    subasset_parent = None
    if status == 'valid' and subasset_longname is not None: # Protocol change.
            # ensure the subasset_longname is valid
            util.validate_subasset_longname(subasset_longname)
            subasset_parent, subasset_longnameM
 = util.parse_subasset_from_asset_name(subasset_longname)
        except exceptions.AssetNameError as e:
            asset = None
            status = 'invalid: bad subasset name'
    reissuance = None
    if status == 'valid':
        call_date, call_price, problems, fee, description, divisible, lock, reset, reissuance, reissued_asset_longname = validate(db, tx['source'], tx['destination'], asset, quantity, divisible, lock, reset, callable_, call_date, call_price, description, subasset_parent, subasseM
t_longname, block_index=tx['block_index'])
        if problems: status = 'invalid: ' + '; '.join(problems)
        if not util.enabled('integer_overflow_fix', block_index=tx['block_index']) and 'total quantity overflow' in problems:
            quantity = 0
    if (status == 'valid') and reset and util.enabled("cip03", tx['block_index']):
        balances_cursor = issuance_parse_cursor.execute('''SELECT * FROM balances WHERE asset = ? AND quantity > 0''', (asset,))
        balances_result = balances_M
        if len(balances_result) <= 1:
            if len(balances_result) == 0:
                issuances_cursor = issuance_parse_cursor.execute('''SELECT * FROM issuances WHERE asset = ? ORDER BY tx_index DESC''', (asset,))
                issuances_result = issuances_cursor.fetchall()
                owner_balance = 0
                owner_address = issuances_result[0]['issuer']
                owner_balance = balances_result[0]["quantity"]
     owner_address = balances_result[0]["address"]
            if owner_address == tx['source']:
                if owner_balance > 0:
                    util.debit(db, tx['source'], asset, owner_balance, 'reset destroy', tx['tx_hash'])

                    bindings = {
                        'tx_index': tx['tx_index'],
                        'tx_hash': tx['tx_hash'],
                        'block_index': tx['block_index'],
                        'source': tx['source'],
                 'asset': asset,
                        'quantity': owner_balance,
                        'tag': "reset",
                        'status': "valid",
                        'reset': True,
                       }
                    sql = 'insert into destructions values(:tx_index, :tx_hash, :block_index, :source, :asset, :quantity, :tag, :status)'
                    issuance_parse_cursor.execute(sql, bindings)
                bindings= {
                    'tx_index': tx['tx_index'],
                   'tx_hash': tx['tx_hash'],
                    'block_index': tx['block_index'],
                    'asset': asset,
                    'quantity': quantity,
                    'divisible': divisible,
                    'source': tx['source'],
                    'issuer': tx['source'],
                    'transfer': False,
                    'callable': callable_,
                    'call_date': call_date,
                    'call_price': call_price,
                    'description': descM
                    'fee_paid': 0,
                    'locked': lock,
                    'status': status,
                    'reset': True,
                    'asset_longname': reissued_asset_longname,
                sql='insert into issuances values(:tx_index, :tx_hash, 0, :block_index, :asset, :quantity, :divisible, :source, :issuer, :transfer, :callable, :call_date, :call_price, :description, :fee_paid, :locked, :status, :asset_longname, :reset)'
                issuM
ance_parse_cursor.execute(sql, bindings)
                # Credit.
                if quantity:
                    util.credit(db, tx['source'], asset, quantity, action="reset issuance", event=tx['tx_hash'])
        if tx['destination']:
            issuer = tx['destination']
            transfer = True
            #quantity = 0
            issuer = tx['source']
            transfer = False
        # Debit fee.
        if status == 'valid':
            util.debit(db, tx['sourM
ce'], config.XCP, fee, action="issuance fee", event=tx['tx_hash'])
        if not isinstance(lock,bool):
            lock = False
        if status == 'valid':
            if (description and description.lower() == 'lock') or lock:
                lock = True
                cursor = db.cursor()
                issuances = list(cursor.execute('''SELECT * FROM issuances \
                                                   WHERE (status = ? AND asset = ?)
                                             M
      ORDER BY tx_index ASC''', ('valid', asset)))
                cursor.close()
                if len(issuances) > 0:
                    description = issuances[-1]['description']  # Use last description
            if not reissuance:
                # Add to table of assets.
                bindings= {
                    'asset_id': str(asset_id),
                    'asset_name': str(asset),
                    'block_index': tx['block_index'],
                    'asset_longname': subasset_longname,
                sql='insert into assets values(:asset_id, :asset_name, :block_index, :asset_longname)'
                issuance_parse_cursor.execute(sql, bindings)
        if status == 'valid' and reissuance:
            # when reissuing, add the asset_longname to the issuances table for API lookups
            asset_longname = reissued_asset_longname
            asset_longname = subasset_longname
        # Add parsed transaction to message-type
        'tx_index': tx['tx_index'],
            'tx_hash': tx['tx_hash'],
            'block_index': tx['block_index'],
            'asset': asset,
            'quantity': quantity,
            'divisible': divisible,
            'source': tx['source'],
            'issuer': issuer,
            'transfer': transfer,
            'callable': callable_,
            'call_date': call_date,
            'call_price': call_price,
            'description': description,
            'fee_paid': fee,
            'locked': loM
            'reset': reset,
            'status': status,
            'asset_longname': asset_longname,
        if "integer overflow" not in status:
            sql='insert into issuances values(:tx_index, :tx_hash, 0, :block_index, :asset, :quantity, :divisible, :source, :issuer, :transfer, :callable, :call_date, :call_price, :description, :fee_paid, :locked, :status, :asset_longname, :reset)'
            issuance_parse_cursor.execute(sql, bindings)
            logger.warn("Not storing M
[issuance] tx [%s]: %s" % (tx['tx_hash'], status))
            logger.debug("Bindings: %s" % (json.dumps(bindings), ))
        if status == 'valid' and quantity:
            util.credit(db, tx['source'], asset, quantity, action="issuance", event=tx['tx_hash'])
        issuance_parse_cursor.close()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
# Filled orders may not be re
opened, so only orders not involving BTC (and so
# which cannot haveM
 expired order matches) may be filled.
logger = logging.getLogger(__name__)
from counterpartylib.lib import config
from counterpartylib.lib import exceptions
from counterpartylib.lib import util
from counterpartylib.lib import backend
from counterpartylib.lib import log
from counterpartylib.lib import message_type
LENGTH = 8 + 8 + 8 + 8 + 2 + 8
    cursor = db.cursor()
or.execute('''CREATE TABLE IF NOT EXISTS orders(
                      tx_index INTEGER UNIQUE,
                      tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      source TEXT,
                      give_asset TEXT,
                      give_quantity INTEGER,
                      give_remaining INTEGER,
                      get_asset TEXT,
                      get_quantity INTEGER,
                      get_remaining INTEGER,
                      expiration INTEGER,
                  expire_index INTEGER,
                      fee_required INTEGER,
                      fee_required_remaining INTEGER,
                      fee_provided INTEGER,
                      fee_provided_remaining INTEGER,
                      status TEXT,
                      FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index),
                      PRIMARY KEY (tx_index, tx_hash))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISM
                      block_index_idx ON orders (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      index_hash_idx ON orders (tx_index, tx_hash)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      expire_idx ON orders (expire_index, status)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      give_status_idx ON orders (give_asset, status)
                   ''')
.execute('''CREATE INDEX IF NOT EXISTS
                      source_give_status_idx ON orders (source, give_asset, status)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      give_get_status_idx ON orders (get_asset, give_asset, status)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON orders (source)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      give_asset_idx ON orM
                   ''')
    cursor.execute('''CREATE TABLE IF NOT EXISTS order_matches(
                      id TEXT PRIMARY KEY,
                      tx0_index INTEGER,
                      tx0_hash TEXT,
                      tx0_address TEXT,
                      tx1_index INTEGER,
                      tx1_hash TEXT,
                      tx1_address TEXT,
                      forward_asset TEXT,
                      forward_quantity INTEGER,
                      baM
                      backward_quantity INTEGER,
                      tx0_block_index INTEGER,
                      tx1_block_index INTEGER,
                      block_index INTEGER,
                      tx0_expiration INTEGER,
                      tx1_expiration INTEGER,
                      match_expire_index INTEGER,
                      fee_paid INTEGER,
                      status TEXT,
                      FOREIGN KEY (tx0_index, tx0_hash, tx0_block_index) REFERENCES transactions(tM
x_index, tx_hash, block_index),
                      FOREIGN KEY (tx1_index, tx1_hash, tx1_block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      match_expire_idx ON order_matches (status, match_expire_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      forward_status_idx ON order_matches (forward_asset, status)
                   ''')
    cursor.execute(''M
'CREATE INDEX IF NOT EXISTS
                      backward_status_idx ON order_matches (backward_asset, status)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      id_idx ON order_matches (id)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      tx0_address_idx ON order_matches (tx0_address)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      tx1_address_idx ON order_matches (tx1_address)
                   ''')
    # Order Expirations
    cursor.execute('''CREATE TABLE IF NOT EXISTS order_expirations(
                      order_index INTEGER PRIMARY KEY,
                      order_hash TEXT UNIQUE,
                      source TEXT,
                      block_index INTEGER,
                      FOREIGN KEY (block_index) REFERENCES blocks(block_index),
                      FOREIGN KEY (order_index, order_hash) REFERENCES orders(tx_index, tx_hash))
                   ''')
    cursor.execute('''M
CREATE INDEX IF NOT EXISTS
                      block_index_idx ON order_expirations (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON order_expirations (source)
                   ''')
    # Order Match Expirations
    cursor.execute('''CREATE TABLE IF NOT EXISTS order_match_expirations(
                      order_match_id TEXT PRIMARY KEY,
                      tx0_address TEXT,
                      tx1_address TEXT,
                    M
  block_index INTEGER,
                      FOREIGN KEY (order_match_id) REFERENCES order_matches(id),
                      FOREIGN KEY (block_index) REFERENCES blocks(block_index))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON order_match_expirations (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      tx0_address_idx ON order_match_expirations (tx0_address)
                   ''')
or.execute('''CREATE INDEX IF NOT EXISTS
                      tx1_address_idx ON order_match_expirations (tx1_address)
                   ''')
def exact_penalty (db, address, block_index, order_match_id):
    # Penalize addresses that don
t make BTC payments. If an address lets an
    # order match expire, expire sell BTC orders from that address.
    cursor = db.cursor()
    bad_orders = list(cursor.execute('''SELECT * FROM orders \
                                        WHERE (source = ? AND gM
ive_asset = ? AND status = ?)''',
                                     (address, config.BTC, 'open')))
    for bad_order in bad_orders:
        cancel_order(db, bad_order, 'expired', block_index)
    if not (block_index >= 314250 or config.TESTNET or config.REGTEST):   # Protocol change.
        # Order matches.
        bad_order_matches = list(cursor.execute('''SELECT * FROM order_matches \
                                                   WHERE ((tx0_address = ? AND forward_asset = ?) OR (tx1_address = ? AND baM
ckward_asset = ?)) AND (status = ?)''',
                                         (address, config.BTC, address, config.BTC, 'pending')))
        for bad_order_match in bad_order_matches:
            cancel_order_match(db, bad_order_match, 'expired', block_index)
def cancel_order (db, order, status, block_index):
    cursor = db.cursor()
    # Update status of order.
        'status': status,
        'tx_hash': order['tx_hash']
    sql='update orders set statuM
s = :status where tx_hash = :tx_hash'
    cursor.execute(sql, bindings)
    log.message(db, block_index, 'update', 'orders', bindings)
    if order['give_asset'] != config.BTC:    # Can
        util.credit(db, order['source'], order['give_asset'], order['give_remaining'], action='cancel order', event=order['tx_hash'])
    if status == 'expired':
        # Record offer expiration.
        bindings = {
            'order_index': order['tx_index'],
            'order_hash': order['tx_hash'],
    'source': order['source'],
            'block_index': block_index
        sql='insert into order_expirations values(:order_index, :order_hash, :source, :block_index)'
        cursor.execute(sql, bindings)
def cancel_order_match (db, order_match, status, block_index):
    '''The only cancelling is an expiration.
    cursor = db.cursor()
    # Skip order matches just expired as a penalty. (Not very efficient.)
    if not (block_index >= 314250 or config.TESTNET or config.RM
EGTEST):   # Protocol change.
        order_matches = list(cursor.execute('''SELECT * FROM order_matches \
                                               WHERE (id = ? AND status = ?)''',
                                            (order_match['id'], 'expired')))
        if order_matches:
            cursor.close()
    # Update status of order match.
        'status': status,
        'order_match_id': order_match['id']
    sql='update order_matches set status = :status whM
ere id = :order_match_id'
    cursor.execute(sql, bindings)
    log.message(db, block_index, 'update', 'order_matches', bindings)
    order_match_id = util.make_id(order_match['tx0_hash'], order_match['tx1_hash'])
    # If tx0 is dead, credit address directly; if not, replenish give remaining, get remaining, and fee required remaining.
    orders = list(cursor.execute('''SELECT * FROM orders \
                                    WHERE tx_index = ?''',
                                 (order_match['tx0_index'],)))M
    assert len(orders) == 1
    tx0_order = orders[0]
    if tx0_order['status'] in ('expired', 'cancelled'):
        tx0_order_status = tx0_order['status']
        if order_match['forward_asset'] != config.BTC:
            util.credit(db, order_match['tx0_address'],
                        order_match['forward_asset'],
                        order_match['forward_quantity'], action='order {}'.format(tx0_order_status), event=order_match['id'])
        tx0_give_remaining = tx0_order['give_remaining'] + orM
der_match['forward_quantity']
        tx0_get_remaining = tx0_order['get_remaining'] + order_match['backward_quantity']
        if tx0_order['get_asset'] == config.BTC and (block_index >= 297000 or config.TESTNET or config.REGTEST):    # Protocol change.
            tx0_fee_required_remaining = tx0_order['fee_required_remaining'] + order_match['fee_paid']
            tx0_fee_required_remaining = tx0_order['fee_required_remaining']
        tx0_order_status = tx0_order['status']
_order_status == 'filled' and util.enabled("reopen_order_when_btcpay_expires_fix", block_index)): #This case could happen if a BTCpay expires and before the expiration, the order was filled by a correct BTCpay
            tx0_order_status = 'open' # So, we have to open the order again
        bindings = {
            'give_remaining': tx0_give_remaining,
            'get_remaining': tx0_get_remaining,
            'status': tx0_order_status,
            'fee_required_remaining': tx0_fee_required_reM
            'tx_hash': order_match['tx0_hash']
        sql='update orders set give_remaining = :give_remaining, get_remaining = :get_remaining, fee_required_remaining = :fee_required_remaining, status = :status where tx_hash = :tx_hash'
        cursor.execute(sql, bindings)
        log.message(db, block_index, 'update', 'orders', bindings)
    # If tx1 is dead, credit address directly; if not, replenish give remaining, get remaining, and fee required remaining.
    orders = list(cursor.execute('M
''SELECT * FROM orders \
                                    WHERE tx_index = ?''',
                                 (order_match['tx1_index'],)))
    assert len(orders) == 1
    tx1_order = orders[0]
    if tx1_order['status'] in ('expired', 'cancelled'):
        tx1_order_status = tx1_order['status']
        if order_match['backward_asset'] != config.BTC:
            util.credit(db, order_match['tx1_address'],
                        order_match['backward_asset'],
                        order_match['backward_quaM
ntity'], action='order {}'.format(tx1_order_status), event=order_match['id'])
        tx1_give_remaining = tx1_order['give_remaining'] + order_match['backward_quantity']
        tx1_get_remaining = tx1_order['get_remaining'] + order_match['forward_quantity']
        if tx1_order['get_asset'] == config.BTC and (block_index >= 297000 or config.TESTNET or config.REGTEST):    # Protocol change.
            tx1_fee_required_remaining = tx1_order['fee_required_remaining'] + order_match['fee_paid']
            tx1_fee_required_remaining = tx1_order['fee_required_remaining']
        tx1_order_status = tx1_order['status']
        if (tx1_order_status == 'filled' and util.enabled("reopen_order_when_btcpay_expires_fix", block_index)): #This case could happen if a BTCpay expires and before the expiration, the order was filled by a correct BTCpay
            tx1_order_status = 'open' # So, we have to open the order again
        bindings = {
            'give_remaining': tx1_give_remaining,
et_remaining': tx1_get_remaining,
            'status': tx1_order_status,
            'fee_required_remaining': tx1_fee_required_remaining,
            'tx_hash': order_match['tx1_hash']
        sql='update orders set give_remaining = :give_remaining, get_remaining = :get_remaining, fee_required_remaining = :fee_required_remaining, status = :status where tx_hash = :tx_hash'
        cursor.execute(sql, bindings)
        log.message(db, block_index, 'update', 'orders', bindings)
    if block_index < 286500M
:    # Protocol change.
        # Sanity check: one of the two must have expired.
        tx0_order_time_left = tx0_order['expire_index'] - block_index
        tx1_order_time_left = tx1_order['expire_index'] - block_index
        assert tx0_order_time_left or tx1_order_time_left
    # Penalize tardiness.
    if block_index >= 313900 or config.TESTNET or config.REGTEST:  # Protocol change.
        if tx0_order['status'] == 'expired' and order_match['forward_asset'] == config.BTC:
            exact_penalty(db, orderM
_match['tx0_address'], block_index, order_match['id'])
        if tx1_order['status'] == 'expired' and order_match['backward_asset'] == config.BTC:
            exact_penalty(db, order_match['tx1_address'], block_index, order_match['id'])
    if block_index >= 310000 or config.TESTNET or config.REGTEST: # Protocol change.
        if not (block_index >= 315000 or config.TESTNET or config.REGTEST):   # Protocol change.
            cursor.execute('''SELECT * FROM transactions\
                       M
       WHERE tx_hash = ?''', (tx0_order['tx_hash'],))
            match(db, list(cursor)[0], block_index)
            cursor.execute('''SELECT * FROM transactions\
                              WHERE tx_hash = ?''', (tx1_order['tx_hash'],))
            match(db, list(cursor)[0], block_index)
    if status == 'expired':
        # Record order match expiration.
        bindings = {
            'order_match_id': order_match['id'],
            'tx0_address': order_match['tx0_address'],
            'tx1_address': orderM
_match['tx1_address'],
            'block_index': block_index
        sql='insert into order_match_expirations values(:order_match_id, :tx0_address, :tx1_address, :block_index)'
        cursor.execute(sql, bindings)
def validate (db, source, give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required, block_index):
    cursor = db.cursor()
    if give_quantity > config.MAX_INT or get_quantity > config.MAX_INT or fee_required > conM
fig.MAX_INT or block_index + expiration > config.MAX_INT:
        problems.append('integer overflow')
    if give_asset == config.BTC and get_asset == config.BTC:
        problems.append('cannot trade {} for itself'.format(config.BTC))
    if not isinstance(give_quantity, int):
        problems.append('give_quantity must be in satoshis')
        return problems
    if not isinstance(get_quantity, int):
        problems.append('get_quantity must be in satoshis')
        return problems
    if not isinstance(fee_reM
        problems.append('fee_required must be in satoshis')
        return problems
    if not isinstance(expiration, int):
        problems.append('expiration must be expressed as an integer block delta')
        return problems
    if give_quantity <= 0: problems.append('non
positive give quantity')
    if get_quantity <= 0: problems.append('non
positive get quantity')
    if fee_required < 0: problems.append('negative fee_required')
    if expiration < 0: problems.append('negative expiration')M
    if expiration == 0 and not (block_index >= 317500 or config.TESTNET or config.REGTEST):   # Protocol change.
        problems.append('zero expiration')
    if not give_quantity or not get_quantity:
        problems.append('zero give or zero get')
    cursor.execute('select * from issuances where (status = ? and asset = ?)', ('valid', give_asset))
    if give_asset not in (config.BTC, config.XCP) and not cursor.fetchall():
        problems.append('no such asset to give ({})'.format(give_asset))
ute('select * from issuances where (status = ? and asset = ?)', ('valid', get_asset))
    if get_asset not in (config.BTC, config.XCP) and not cursor.fetchall():
        problems.append('no such asset to get ({})'.format(get_asset))
    if expiration > config.MAX_EXPIRATION:
        problems.append('expiration overflow')
def compose (db, source, give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required):
    cursor = db.cursor()
    # resolve subassets
  give_asset = util.resolve_subasset_longname(db, give_asset)
    get_asset = util.resolve_subasset_longname(db, get_asset)
    # Check balance.
    if give_asset != config.BTC:
        balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, give_asset)))
        if (not balances or balances[0]['quantity'] < give_quantity):
            raise exceptions.ComposeError('insufficient funds')
    problems = validate(db, source, give_asset, give_quantity, get_asset, get_quM
antity, expiration, fee_required, util.CURRENT_BLOCK_INDEX)
    if problems: raise exceptions.ComposeError(problems)
    give_id = util.get_asset_id(db, give_asset, util.CURRENT_BLOCK_INDEX)
    get_id = util.get_asset_id(db, get_asset, util.CURRENT_BLOCK_INDEX)
    data = message_type.pack(ID)
    data += struct.pack(FORMAT, give_id, give_quantity, get_id, get_quantity,
                        expiration, fee_required)
    return (source, [], data)
def parse (db, tx, message):
cursor = db.cursor()
    # Unpack message.
        if len(message) != LENGTH:
            raise exceptions.UnpackError
        give_id, give_quantity, get_id, get_quantity, expiration, fee_required = struct.unpack(FORMAT, message)
        give_asset = util.get_asset_name(db, give_id, tx['block_index'])
        get_asset = util.get_asset_name(db, get_id, tx['block_index'])
        status = 'open'
    except (exceptions.UnpackError, exceptions.AssetNameError, struct.error) as e:
        give_asset, give_quaM
ntity, get_asset, get_quantity, expiration, fee_required = 0, 0, 0, 0, 0, 0
        status = 'invalid: could not unpack'
    if status == 'open':
            price = util.price(get_quantity, give_quantity)
        except ZeroDivisionError:
            price = 0
        order_parse_cursor.execute('''SELECT * FROM balances \
                                      WHERE (address = ? AND asset = ?)''', (tx['source'], give_asset))
        balances = list(order_parse_cursorM
        if give_asset != config.BTC:
            if not balances:
                give_quantity = 0
                balance = balances[0]['quantity']
                if balance < give_quantity:
                    give_quantity = balance
                    get_quantity = int(price * give_quantity)
        problems = validate(db, tx['source'], give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required, tx['block_index'])
        if problems: status = 'invalid: ' + '; '.join(prM
        if util.enabled('btc_order_minimum'):
            min_btc_quantity = 0.001 * config.UNIT  # 0.001 BTC
            if util.enabled('btc_order_minimum_adjustment_1'):
                min_btc_quantity = 0.00001 * config.UNIT  # 0.00001 BTC
            if (give_asset == config.BTC and give_quantity < min_btc_quantity) or (get_asset == config.BTC and get_quantity < min_btc_quantity):
                if problems:
                    status += '; btc order below minimum'
                else:
         status = 'invalid: btc order below minimum'
    # Debit give quantity. (Escrow.)
    if status == 'open':
        if give_asset != config.BTC:  # No need (or way) to debit BTC.
            util.debit(db, tx['source'], give_asset, give_quantity, action='open order', event=tx['tx_hash'])
    # Add parsed transaction to message-type
        'tx_index': tx['tx_index'],
        'tx_hash': tx['tx_hash'],
        'block_index': tx['block_index'],
        'source': tx['source']M
        'give_asset': give_asset,
        'give_quantity': give_quantity,
        'give_remaining': give_quantity,
        'get_asset': get_asset,
        'get_quantity': get_quantity,
        'get_remaining': get_quantity,
        'expiration': expiration,
        'expire_index': tx['block_index'] + expiration,
        'fee_required': fee_required,
        'fee_required_remaining': fee_required,
        'fee_provided': tx['fee'],
        'fee_provided_remaining': tx['fee'],
        'status': status,
 "integer overflow" not in status:
        sql = 'insert into orders values(:tx_index, :tx_hash, :block_index, :source, :give_asset, :give_quantity, :give_remaining, :get_asset, :get_quantity, :get_remaining, :expiration, :expire_index, :fee_required, :fee_required_remaining, :fee_provided, :fee_provided_remaining, :status)'
        order_parse_cursor.execute(sql, bindings)
        logger.warn("Not storing [order] tx [%s]: %s" % (tx['tx_hash'], status))
        logger.debug("Bindings: %s" % (json.dumps(biM
    if status == 'open' and tx['block_index'] != config.MEMPOOL_BLOCK_INDEX:
        match(db, tx)
    order_parse_cursor.close()
def match (db, tx, block_index=None):
    cursor = db.cursor()
    # Get order in question.
    orders = list(cursor.execute('''SELECT * FROM orders\
                                    WHERE (tx_index = ? AND status = ?)''', (tx['tx_index'], 'open')))
        cursor.close()
        assert len(orders) == 1
    cursor.execute('''SELECT * FROM orders \
                      WHERE (give_asset=? AND get_asset=? AND status=? AND tx_hash != ?)''',
                   (tx1['get_asset'], tx1['give_asset'], 'open', tx1['tx_hash']))
    tx1_give_remaining = tx1['give_remaining']
    tx1_get_remaining = tx1['get_remaining']
    order_matches = cursor.fetchall()
    if tx['block_index'] > 284500 or config.TESTNET or config.REGTEST:  # Protocol change.
        order_matches = sorted(order_matches, key=lambda x: x['tM
x_index'])                              # Sort by tx index second.
        order_matches = sorted(order_matches, key=lambda x: util.price(x['get_quantity'], x['give_quantity']))   # Sort by price first.
    # Get fee remaining.
    tx1_fee_required_remaining = tx1['fee_required_remaining']
    tx1_fee_provided_remaining = tx1['fee_provided_remaining']
    tx1_status = tx1['status']
    for tx0 in order_matches:
        order_match_id = util.make_id(tx0['tx_hash'], tx1['tx_hash'])
        if not block_index:
       block_index = max(tx0['block_index'], tx1['block_index'])
        if tx1_status != 'open': break
        logger.debug('Considering: ' + tx0['tx_hash'])
        tx0_give_remaining = tx0['give_remaining']
        tx0_get_remaining = tx0['get_remaining']
        # Ignore previous matches. (Both directions, just to be sure.)
        cursor.execute('''SELECT * FROM order_matches
                          WHERE id = ? ''', (util.make_id(tx0['tx_hash'], tx1['tx_hash']), ))
        if list(cursor):
ger.debug('Skipping: previous match')
            continue
        cursor.execute('''SELECT * FROM order_matches
                          WHERE id = ? ''', (util.make_id(tx1['tx_hash'], tx0['tx_hash']), ))
        if list(cursor):
            logger.debug('Skipping: previous match')
            continue
        # Get fee provided remaining.
        tx0_fee_required_remaining = tx0['fee_required_remaining']
        tx0_fee_provided_remaining = tx0['fee_provided_remaining']
        # Make sure that that both orderM
s still have funds remaining (if order involves BTC, and so cannot be
        if tx0['give_asset'] == config.BTC or tx0['get_asset'] == config.BTC: # Gratuitous
            if tx0_give_remaining <= 0 or tx1_give_remaining <= 0:
                logger.debug('Skipping: negative give quantity remaining')
                continue
            if block_index >= 292000 and block_index <= 310500 and not config.TESTNET or config.REGTEST:  # Protocol changes
                if tx0_get_remaining <= 0 or tx1_getM
                    logger.debug('Skipping: negative get quantity remaining')
                    continue
            if block_index >= 294000 or config.TESTNET or config.REGTEST:  # Protocol change.
                if tx0['fee_required_remaining'] < 0:
                    logger.debug('Skipping: negative tx0 fee required remaining')
                    continue
                if tx0['fee_provided_remaining'] < 0:
                    logger.debug('Skipping: negative tx0 fee provided remaining')
                    continue
                if tx1_fee_provided_remaining < 0:
                    logger.debug('Skipping: negative tx1 fee provided remaining')
                    continue
                if tx1_fee_required_remaining < 0:
                    logger.debug('Skipping: negative tx1 fee required remaining')
                    continue
        # If the prices agree, make the trade. The found order sets the price,
        # and they trade as much as they can.
        tx0_price = util.price(tx0['get_qM
uantity'], tx0['give_quantity'])
        tx1_price = util.price(tx1['get_quantity'], tx1['give_quantity'])
        tx1_inverse_price = util.price(tx1['give_quantity'], tx1['get_quantity'])
        # Protocol change.
        if tx['block_index'] < 286000: tx1_inverse_price = util.price(1, tx1_price)
        logger.debug('Tx0 Price: {}; Tx1 Inverse Price: {}'.format(float(tx0_price), float(tx1_inverse_price)))
        if tx0_price > tx1_inverse_price:
            logger.debug('Skipping: price mismatch.')
            logger.debug('Potential forward quantities: {}, {}'.format(tx0_give_remaining, int(util.price(tx1_give_remaining, tx0_price))))
            forward_quantity = int(min(tx0_give_remaining, int(util.price(tx1_give_remaining, tx0_price))))
            logger.debug('Forward Quantity: {}'.format(forward_quantity))
            backward_quantity = round(forward_quantity * tx0_price)
            logger.debug('Backward Quantity: {}'.format(backward_quantity))
            if not forward_quantity:
    logger.debug('Skipping: zero forward quantity.')
                continue
            if block_index >= 286500 or config.TESTNET or config.REGTEST:    # Protocol change.
                if not backward_quantity:
                    logger.debug('Skipping: zero backward quantity.')
                    continue
            forward_asset, backward_asset = tx1['get_asset'], tx1['give_asset']
            if block_index >= 313900 or config.TESTNET or config.REGTEST: # Protocol change.
                min_btc_quantiM
ty = 0.001 * config.UNIT  # 0.001 BTC
                if (forward_asset == config.BTC and forward_quantity <= min_btc_quantity) or (backward_asset == config.BTC and backward_quantity <= min_btc_quantity):
                    logger.debug('Skipping: below minimum {} quantity'.format(config.BTC))
                    continue
            # Check and update fee remainings.
            if block_index >= 286500 or config.TESTNET or config.REGTEST: # Protocol change. Deduct fee_required from provided_M
remaining, etc., if possible (else don
                if tx1['get_asset'] == config.BTC:
                    if block_index >= 310500 or config.TESTNET or config.REGTEST:     # Protocol change.
                        fee = int(tx1['fee_required'] * util.price(backward_quantity, tx1['give_quantity']))
                    else:
                        fee = int(tx1['fee_required_remaining'] * util.price(forward_quantity, tx1_get_remaining))
                    logger.debug('Tx0 fee provided remaining:M
 {}; required fee: {}'.format(tx0_fee_provided_remaining / config.UNIT, fee / config.UNIT))
                    if tx0_fee_provided_remaining < fee:
                        logger.debug('Skipping: tx0 fee provided remaining is too low.')
                        continue
                    else:
                        tx0_fee_provided_remaining -= fee
                        if block_index >= 287800 or config.TESTNET or config.REGTEST:  # Protocol change.
                            tx1_fee_required_remaining -= fM
                elif tx1['give_asset'] == config.BTC:
                    if block_index >= 310500 or config.TESTNET or config.REGTEST:      # Protocol change.
                        fee = int(tx0['fee_required'] * util.price(backward_quantity, tx0['give_quantity']))
                    else:
                        fee = int(tx0['fee_required_remaining'] * util.price(backward_quantity, tx0_get_remaining))
                    logger.debug('Tx1 fee provided remaining: {}; required fee: {}'.format(tx1_fee_provM
ided_remaining / config.UNIT, fee / config.UNIT))
                    if tx1_fee_provided_remaining < fee:
                        logger.debug('Skipping: tx1 fee provided remaining is too low.')
                        continue
                    else:
                        tx1_fee_provided_remaining -= fee
                        if block_index >= 287800 or config.TESTNET or config.REGTEST:  # Protocol change.
                            tx0_fee_required_remaining -= fee
            else:   # Don
                if tx1['get_asset'] == config.BTC:
                    if tx0_fee_provided_remaining < tx1['fee_required']: continue
                elif tx1['give_asset'] == config.BTC:
                    if tx1_fee_provided_remaining < tx0['fee_required']: continue
            if config.BTC in (tx1['give_asset'], tx1['get_asset']):
                status = 'pending'
                status = 'completed'
                # Credit.
                util.credit(db, tx1['source'], tx1['get_asset'],
                                   forward_quantity, action='order match', event=order_match_id)
                util.credit(db, tx0['source'], tx0['get_asset'],
                                    backward_quantity, action='order match', event=order_match_id)
            # Debit the order, even if it involves giving bitcoins, and so one
            # can't debit the sending account.
            # Get remainings may be negative.
            tx0_give_remaining -= forward_quantity
            tx0_get_remaining -= baM
            tx1_give_remaining -= backward_quantity
            tx1_get_remaining -= forward_quantity
            # Update give_remaining, get_remaining.
            tx0_status = 'open'
            if tx0_give_remaining <= 0 or (tx0_get_remaining <= 0 and (block_index >= 292000 or config.TESTNET or config.REGTEST)):    # Protocol change
                if tx0['give_asset'] != config.BTC and tx0['get_asset'] != config.BTC:
                    # Fill order, and recredit give_remainiM
                    tx0_status = 'filled'
                    util.credit(db, tx0['source'], tx0['give_asset'], tx0_give_remaining, event=tx1['tx_hash'], action='filled')
            bindings = {
                'give_remaining': tx0_give_remaining,
                'get_remaining': tx0_get_remaining,
                'fee_required_remaining': tx0_fee_required_remaining,
                'fee_provided_remaining': tx0_fee_provided_remaining,
                'status': tx0_status,
                'tx_hash': tx0['tx_hM
            sql='update orders set give_remaining = :give_remaining, get_remaining = :get_remaining, fee_required_remaining = :fee_required_remaining, fee_provided_remaining = :fee_provided_remaining, status = :status where tx_hash = :tx_hash'
            cursor.execute(sql, bindings)
            log.message(db, block_index, 'update', 'orders', bindings)
            if tx1_give_remaining <= 0 or (tx1_get_remaining <= 0 and (block_index >= 292000 or config.TESTNET or config.REGTM
EST)):    # Protocol change
                if tx1['give_asset'] != config.BTC and tx1['get_asset'] != config.BTC:
                    # Fill order, and recredit give_remaining.
                    tx1_status = 'filled'
                    util.credit(db, tx1['source'], tx1['give_asset'], tx1_give_remaining, event=tx0['tx_hash'], action='filled')
            bindings = {
                'give_remaining': tx1_give_remaining,
                'get_remaining': tx1_get_remaining,
                'fee_required_remaining'M
: tx1_fee_required_remaining,
                'fee_provided_remaining': tx1_fee_provided_remaining,
                'status': tx1_status,
                'tx_hash': tx1['tx_hash']
            sql='update orders set give_remaining = :give_remaining, get_remaining = :get_remaining, fee_required_remaining = :fee_required_remaining, fee_provided_remaining = :fee_provided_remaining, status = :status where tx_hash = :tx_hash'
            cursor.execute(sql, bindings)
            log.message(db, block_index,M
 'update', 'orders', bindings)
            # Calculate when the match will expire.
            if block_index >= 308000 or config.TESTNET or config.REGTEST:      # Protocol change.
                match_expire_index = block_index + 20
            elif block_index >= 286500 or config.TESTNET or config.REGTEST:    # Protocol change.
                match_expire_index = block_index + 10
                match_expire_index = min(tx0['expire_index'], tx1['expire_index'])
            # Record order matM
            bindings = {
                'id': util.make_id(tx0['tx_hash'], tx['tx_hash']),
                'tx0_index': tx0['tx_index'],
                'tx0_hash': tx0['tx_hash'],
                'tx0_address': tx0['source'],
                'tx1_index': tx1['tx_index'],
                'tx1_hash': tx1['tx_hash'],
                'tx1_address': tx1['source'],
                'forward_asset': forward_asset,
                'forward_quantity': forward_quantity,
                'backward_asset': backward_asset,
                'backward_quantity': backward_quantity,
                'tx0_block_index': tx0['block_index'],
                'tx1_block_index': tx1['block_index'],
                'block_index': block_index,
                'tx0_expiration': tx0['expiration'],
                'tx1_expiration': tx1['expiration'],
                'match_expire_index': match_expire_index,
                'fee_paid': fee,
                'status': status,
            sql='insert into order_matches values(:id, :tx0_indexM
, :tx0_hash, :tx0_address, :tx1_index, :tx1_hash, :tx1_address, :forward_asset, :forward_quantity, :backward_asset, :backward_quantity, :tx0_block_index, :tx1_block_index, :block_index, :tx0_expiration, :tx1_expiration, :match_expire_index, :fee_paid, :status)'
            cursor.execute(sql, bindings)
            if tx1_status == 'filled':
                break
def expire (db, block_index):
    cursor = db.cursor()
    # Expire orders and give refunds for the quantity give_remainiM
ng (if non-zero; if not BTC).
    cursor.execute('''SELECT * FROM orders \
                      WHERE (status = ? AND expire_index < ?)''', ('open', block_index))
    orders = list(cursor)
    for order in orders:
        cancel_order(db, order, 'expired', block_index)
    # Expire order_matches for BTC with no BTC.
    cursor.execute('''SELECT * FROM order_matches \
                      WHERE (status = ? and match_expire_index < ?)''', ('pending', block_index))
    order_matches = list(cursor)
ch in order_matches:
        cancel_order_match(db, order_match, 'expired', block_index)
        # Expire btc sell order if match expires
        if util.enabled('btc_sell_expire_on_match_expire'):
            # Check for other pending order matches involving either tx0_hash or tx1_hash
            bindings = {
                'status': 'pending',
                'tx0_hash': order_match['tx0_hash'],
                'tx1_hash': order_match['tx1_hash']
            sql='select * from order_matches whereM
 status = :status and ((tx0_hash in (:tx0_hash, :tx1_hash)) or ((tx1_hash in (:tx0_hash, :tx1_hash))))'
            cursor.execute(sql, bindings)
            order_matches_pending = cursor.fetchall()
            # Set BTC sell order status as expired only if there are no pending order matches
            if len(order_matches_pending) == 0:
                if order_match['backward_asset'] == "BTC" and order_match['status'] == "expired":
                    cursor.execute('''SELECT * FROM orders \
                   M
                   WHERE tx_hash = ?''', (order_match['tx1_hash'],))
                    cancel_order(db, list(cursor)[0], 'expired', block_index)
                if order_match['forward_asset'] == "BTC" and order_match['status'] == "expired":
                    cursor.execute('''SELECT * FROM orders \
                                      WHERE tx_hash = ?''', (order_match['tx0_hash'],))
                    cancel_order(db, list(cursor)[0], 'expired', block_index)
    if block_index >= 315000 or config.TESTNET oM
r config.REGTEST: # Protocol change.
        for order_match in order_matches:
            cursor.execute('''SELECT * FROM transactions\
                              WHERE tx_hash = ?''', (order_match['tx0_hash'],))
            match(db, list(cursor)[0], block_index)
            cursor.execute('''SELECT * FROM transactions\
                              WHERE tx_hash = ?''', (order_match['tx1_hash'],))
            match(db, list(cursor)[0], block_index)
xpandtab shiftwidth=4 softtabstop=4
Transaction 1: rps (Open the game)
source: address used to play the game
wager: amount to bet
move_random_hash: sha256(sha256(move + random)) (stored as bytes, 16 bytes random)
possible_moves: arbitrary odd number >= 3
expiration: how many blocks the game is valid
Matching conditions:
- tx0_possible_moves = tx1_possible_moves
- tx0_wager = tx1_wager
Transaction 2:  rpsresolve (Resolve the game)
source: same address as first transaM
random: 16 bytes random
move: the move number
rps_match_id: matching id
from counterpartylib.lib import config
from counterpartylib.lib import exceptions
from counterpartylib.lib import util
from counterpartylib.lib import log
from counterpartylib.lib import message_type
# possible_moves wager move_random_hash expiration
LENGTH = 2 + 8 + 32 + 4
def initialise (db):
    cursor = db.cursM
    # RPS (Rock-Paper-Scissors)
    cursor.execute('''CREATE TABLE IF NOT EXISTS rps(
                      tx_index INTEGER UNIQUE,
                      tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      source TEXT,
                      possible_moves INTEGER,
                      wager INTEGER,
                      move_random_hash TEXT,
                      expiration INTEGER,
                      expire_index INTEGER,
                      status TEXT,
           FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index),
                      PRIMARY KEY (tx_index, tx_hash))
                  ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON rps (source)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      matching_idx ON rps (wager, possible_moves)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                   M
   status_idx ON rps (status)
                   ''')
    cursor.execute('''CREATE TABLE IF NOT EXISTS rps_matches(
                      id TEXT PRIMARY KEY,
                      tx0_index INTEGER,
                      tx0_hash TEXT,
                      tx0_address TEXT,
                      tx1_index INTEGER,
                      tx1_hash TEXT,
                      tx1_address TEXT,
                      tx0_move_random_hash TEXT,
                      tx1_move_random_hash TEXT,
              wager INTEGER,
                      possible_moves INTEGER,
                      tx0_block_index INTEGER,
                      tx1_block_index INTEGER,
                      block_index INTEGER,
                      tx0_expiration INTEGER,
                      tx1_expiration INTEGER,
                      match_expire_index INTEGER,
                      status TEXT,
                      FOREIGN KEY (tx0_index, tx0_hash, tx0_block_index) REFERENCES transactions(tx_index, tx_hash, block_index),
                     FOREIGN KEY (tx1_index, tx1_hash, tx1_block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      rps_match_expire_idx ON rps_matches (status, match_expire_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      rps_tx0_address_idx ON rps_matches (tx0_address)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
        rps_tx1_address_idx ON rps_matches (tx1_address)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      status_idx ON rps_matches (status)
                   ''')
    # RPS Expirations
    cursor.execute('''CREATE TABLE IF NOT EXISTS rps_expirations(
                      rps_index INTEGER PRIMARY KEY,
                      rps_hash TEXT UNIQUE,
                      source TEXT,
                      block_index INTEGER,
                      FOREIGN KEY (block_indeM
x) REFERENCES blocks(block_index),
                      FOREIGN KEY (rps_index, rps_hash) REFERENCES rps(tx_index, tx_hash))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON rps_expirations (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON rps_expirations (source)
                   ''')
    # RPS Match Expirations
    cursor.execute('''CREATE TABLE IF NOT EXISTS rps_match_expiM
                      rps_match_id TEXT PRIMARY KEY,
                      tx0_address TEXT,
                      tx1_address TEXT,
                      block_index INTEGER,
                      FOREIGN KEY (rps_match_id) REFERENCES rps_matches(id),
                      FOREIGN KEY (block_index) REFERENCES blocks(block_index))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON rps_match_expirations (block_index)
                   ''')
cursor.execute('''CREATE INDEX IF NOT EXISTS
                      tx0_address_idx ON rps_match_expirations (tx0_address)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      tx1_address_idx ON rps_match_expirations (tx1_address)
                   ''')
def cancel_rps (db, rps, status, block_index):
    cursor = db.cursor()
    # Update status of rps.
        'status': status,
        'tx_hash': rps['tx_hash']
    sql='''UPDATE rps SET status = :stM
atus WHERE tx_hash = :tx_hash'''
    cursor.execute(sql, bindings)
    log.message(db, block_index, 'update', 'rps', bindings)
    util.credit(db, rps['source'], 'XCP', rps['wager'], action='recredit wager', event=rps['tx_hash'])
def update_rps_match_status (db, rps_match, status, block_index):
    cursor = db.cursor()
    if status in ['expired', 'concluded: tie']:
        # Recredit tx0 address.
        util.credit(db, rps_match['tx0_address'], 'XCP',
                    rps_match['wager'],M
 action='recredit wager', event=rps_match['id'])
        # Recredit tx1 address.
        util.credit(db, rps_match['tx1_address'], 'XCP',
                    rps_match['wager'], action='recredit wager', event=rps_match['id'])
    elif status.startswith('concluded'):
        # Credit the winner
        winner = rps_match['tx0_address'] if status == 'concluded: first player wins' else rps_match['tx1_address']
        util.credit(db, winner, 'XCP',
                    2 * rps_match['wager'], action='wins', event=rps_mM
    # Update status of rps match.
        'status': status,
        'rps_match_id': rps_match['id']
    sql='UPDATE rps_matches SET status = :status WHERE id = :rps_match_id'
    cursor.execute(sql, bindings)
    log.message(db, block_index, 'update', 'rps_matches', bindings)
def validate (db, source, possible_moves, wager, move_random_hash, expiration, block_index):
    if util.enabled('disable_rps'):
        problems.append('rps disabled'M
    if not isinstance(possible_moves, int):
        problems.append('possible_moves must be a integer')
        return problems
    if not isinstance(wager, int):
        problems.append('wager must be in satoshis')
        return problems
    if not isinstance(expiration, int):
        problems.append('expiration must be expressed as an integer block delta')
        return problems
    if not all(c in string.hexdigits for c in move_random_hash):
        problems.append('move_random_hash must be an hexadecimal M
        return problems
    move_random_hash_bytes = binascii.unhexlify(move_random_hash)
    if possible_moves < 3:
        problems.append('possible moves must be at least 3')
    if possible_moves % 2 == 0:
        problems.append('possible moves must be odd')
        problems.append('non
    if expiration < 0: problems.append('negative expiration')
    if expiration == 0 and not (block_index >= 317500 or config.TESTNET or config.REGTEST):   # Protocol change.
    problems.append('zero expiration')
    if expiration > config.MAX_EXPIRATION:
        problems.append('expiration overflow')
    if len(move_random_hash_bytes) != 32:
        problems.append('move_random_hash must be 32 bytes in hexadecimal format')
def compose(db, source, possible_moves, wager, move_random_hash, expiration):
    problems = validate(db, source, possible_moves, wager, move_random_hash, expiration, util.CURRENT_BLOCK_INDEX)
    if problems: raise exceptions.ComposeError(prM
    data = message_type.pack(ID)
    data += struct.pack(FORMAT, possible_moves, wager, binascii.unhexlify(move_random_hash), expiration)
    return (source, [], data)
def parse(db, tx, message):
    rps_parse_cursor = db.cursor()
    # Unpack message.
        if len(message) != LENGTH:
            raise exceptions.UnpackError
        (possible_moves, wager, move_random_hash, expiration) = struct.unpack(FORMAT, message)
        status = 'open'
    except (exceptions.UnpackError, struct.error):
       (possible_moves, wager, move_random_hash, expiration) = 0, 0, '', 0
        status = 'invalid: could not unpack'
    if status == 'open':
        move_random_hash = binascii.hexlify(move_random_hash).decode('utf8')
        rps_parse_cursor.execute('''SELECT * FROM balances \
                                    WHERE (address = ? AND asset = ?)''', (tx['source'], 'XCP'))
        balances = list(rps_parse_cursor)
        if not balances:
            wager = 0
e = balances[0]['quantity']
            if balance < wager:
                wager = balance
        problems = validate(db, tx['source'], possible_moves, wager, move_random_hash, expiration, tx['block_index'])
        if problems: status = 'invalid: {}'.format(', '.join(problems))
    # Debit quantity wagered. (Escrow.)
    if status == 'open':
        util.debit(db, tx['source'], 'XCP', wager, action="open RPS", event=tx['tx_hash'])
    # Add parsed transaction to message-type
        'tx_index': tx['tx_index'],
        'tx_hash': tx['tx_hash'],
        'block_index': tx['block_index'],
        'source': tx['source'],
        'possible_moves': possible_moves,
        'wager': wager,
        'move_random_hash': move_random_hash,
        'expiration': expiration,
        'expire_index': tx['block_index'] + expiration,
        'status': status,
    sql = '''INSERT INTO rps VALUES (:tx_index, :tx_hash, :block_index, :source, :possible_moves, :wager, :move_random_hash, :expiration, :exM
pire_index, :status)'''
    rps_parse_cursor.execute(sql, bindings)
    if status == 'open':
        match(db, tx, tx['block_index'])
    rps_parse_cursor.close()
def match (db, tx, block_index):
    cursor = db.cursor()
    # Get rps in question.
    rps = list(cursor.execute('''SELECT * FROM rps WHERE tx_index = ? AND status = ?''', (tx['tx_index'], 'open')))
        cursor.close()
        assert len(rps) == 1
    possible_moves = tx1['posM
    wager = tx1['wager']
    tx1_status = 'open'
    bindings = (possible_moves, 'open', wager, tx1['source'])
    # dont match twice same RPS
    already_matched = []
    old_rps_matches = cursor.execute('''SELECT * FROM rps_matches WHERE tx0_hash = ? OR tx1_hash = ?''', (tx1['tx_hash'], tx1['tx_hash']))
    for old_rps_match in old_rps_matches:
        counter_tx_hash = old_rps_match['tx1_hash'] if tx1['tx_hash'] == old_rps_match['tx0_hash'] else old_rps_match['tx0_hash']
  already_matched.append(counter_tx_hash)
    already_matched_cond = ''
    if already_matched:
        already_matched_cond = '''AND tx_hash NOT IN ({})'''.format(','.join(['?' for e in range(0, len(already_matched))]))
        bindings += tuple(already_matched)
    sql = '''SELECT * FROM rps WHERE (possible_moves = ? AND status = ? AND wager = ? AND source != ? {}) ORDER BY tx_index LIMIT 1'''.format(already_matched_cond)
    rps_matches = list(cursor.execute(sql, bindings))
        # update status
        for txn in [tx0, tx1]:
            bindings = {
                'status': 'matched',
                'tx_index': txn['tx_index']
            cursor.execute('''UPDATE rps SET status = :status WHERE tx_index = :tx_index''', bindings)
            log.message(db, block_index, 'update', 'rps', bindings)
        bindings = {
            'id': util.make_id(tx0['tx_hash'], tx1['tx_hash']),
            'tx0_index': tx0['tx_index'],
            'tx0_hash': tx0['tx_M
            'tx0_address': tx0['source'],
            'tx1_index': tx1['tx_index'],
            'tx1_hash': tx1['tx_hash'],
            'tx1_address': tx1['source'],
            'tx0_move_random_hash': tx0['move_random_hash'],
            'tx1_move_random_hash': tx1['move_random_hash'],
            'wager': wager,
            'possible_moves': possible_moves,
            'tx0_block_index': tx0['block_index'],
            'tx1_block_index': tx1['block_index'],
            'block_index': block_index,
    'tx0_expiration': tx0['expiration'],
            'tx1_expiration': tx1['expiration'],
            'match_expire_index': block_index + 20,
            'status': 'pending'
        sql = '''INSERT INTO rps_matches VALUES (:id, :tx0_index, :tx0_hash, :tx0_address, :tx1_index, :tx1_hash, :tx1_address,
                                                 :tx0_move_random_hash, :tx1_move_random_hash, :wager, :possible_moves,
                                                 :tx0_block_index, :tx1_block_index, :blM
ock_index, :tx0_expiration, :tx1_expiration,
                                                 :match_expire_index, :status)'''
        cursor.execute(sql, bindings)
def expire (db, block_index):
    cursor = db.cursor()
    # Expire rps and give refunds for the quantity wager.
    cursor.execute('''SELECT * FROM rps WHERE (status = ? AND expire_index < ?)''', ('open', block_index))
    for rps in cursor.fetchall():
        cancel_rps(db, rps, 'expired', block_index)
        # Record rps expirM
        bindings = {
            'rps_index': rps['tx_index'],
            'rps_hash': rps['tx_hash'],
            'source': rps['source'],
            'block_index': block_index
        sql = '''INSERT INTO rps_expirations VALUES (:rps_index, :rps_hash, :source, :block_index)'''
        cursor.execute(sql, bindings)
    # Expire rps matches
    expire_bindings = ('pending', 'pending and resolved', 'resolved and pending', block_index)
    cursor.execute('''SELECT * FROM rps_matches WHERE (status IM
N (?, ?, ?) AND match_expire_index < ?)''', expire_bindings)
    for rps_match in cursor.fetchall():
        new_rps_match_status = 'expired'
        # pending loses against resolved
        if rps_match['status'] == 'pending and resolved':
            new_rps_match_status = 'concluded: second player wins'
        elif rps_match['status'] == 'resolved and pending':
            new_rps_match_status = 'concluded: first player wins'
        update_rps_match_status(db, rps_match, new_rps_match_status, block_index)
      # Record rps match expiration.
        bindings = {
            'rps_match_id': rps_match['id'],
            'tx0_address': rps_match['tx0_address'],
            'tx1_address': rps_match['tx1_address'],
            'block_index': block_index
        sql = '''INSERT INTO rps_match_expirations VALUES (:rps_match_id, :tx0_address, :tx1_address, :block_index)'''
        cursor.execute(sql, bindings)
        # Rematch not expired and not resolved RPS
        if new_rps_match_status == 'expired':
     sql = '''SELECT * FROM rps WHERE tx_hash IN (?, ?) AND status = ? AND expire_index >= ?'''
            bindings = (rps_match['tx0_hash'], rps_match['tx1_hash'], 'matched', block_index)
            matched_rps = list(cursor.execute(sql, bindings))
            for rps in matched_rps:
                cursor.execute('''UPDATE rps SET status = ? WHERE tx_index = ?''', ('open', rps['tx_index']))
                # Re-debit XCP refund by close_rps_match.
                util.debit(db, rps['source'], 'XCP', rps['wager'M
], action='reopen RPS after matching expiration', event=rps_match['id'])
                # Rematch
                match(db, {'tx_index': rps['tx_index']}, block_index)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
logger = logging.getLogger(__name__)
from counterpartylib.lib import (config, exceptions, util, message_type)
# move random rps_match_id
LENGTH = 2 + 16 + 32 + 32
def initialise (db):
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS rpsresolves(
                      tx_index INTEGER PRIMARY KEY,
                      tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      source TEXT,
                      move INTEGER,
                      random TEXT,
                      rps_match_id TEXT,
                      status TEXT,
                      FOREIGN KEY (tx_indeM
x, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON rpsresolves (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON rpsresolves (source)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      rps_match_id_idx ON rpsresolves (rps_match_id)
                   ''')
ef validate (db, source, move, random, rps_match_id):
    rps_match = None
    if not isinstance(move, int):
        problems.append('move must be a integer')
        return None, None, problems
    if not all(c in string.hexdigits for c in random):
        problems.append('random must be an hexadecimal string')
        return None, None, problems
    random_bytes = binascii.unhexlify(random)
    if len(random_bytes) != 16:
        problems.append('random must be 16 bytes in hexadecimal format'M
        return None, None, problems
    cursor = db.cursor()
    rps_matches = list(cursor.execute('''SELECT * FROM rps_matches WHERE id = ?''', (rps_match_id,)))
    if len(rps_matches) == 0:
        problems.append('no such rps match')
        return None, rps_match, problems
    elif len(rps_matches) > 1:
        assert False
    rps_match = rps_matches[0]
        problems.append('move must be greater than 0')
    elif move > rps_match['possible_moves']:
        problems.apM
pend('move must be lower than {}'.format(rps_match['possible_moves']))
    if source not in [rps_match['tx0_address'], rps_match['tx1_address']]:
        problems.append('invalid source address')
        return None, rps_match, problems
    if rps_match['tx0_address'] == source:
        rps_match_status = ['pending', 'pending and resolved']
        rps_match_status = ['pending', 'resolved and pending']
    move_random_hash = util.dhash(random_bytes + int(move).to_bytes(2M
    move_random_hash = binascii.hexlify(move_random_hash).decode('utf-8')
    if rps_match['tx{}_move_random_hash'.format(txn)] != move_random_hash:
        problems.append('invalid move or random value')
        return txn, rps_match, problems
    if rps_match['status'] == 'expired':
        problems.append('rps match expired')
    elif rps_match['status'].startswith('concluded'):
        problems.append('rps match concluded')
    elif rps_match['status'].startswith('invalid'):
s.append('rps match invalid')
    elif rps_match['status'] not in rps_match_status:
        problems.append('rps already resolved')
    return txn, rps_match, problems
def compose (db, source, move, random, rps_match_id):
    tx0_hash, tx1_hash = util.parse_id(rps_match_id)
    txn, rps_match, problems = validate(db, source, move, random, rps_match_id)
    if problems: raise exceptions.ComposeError(problems)
    # Warn if down to the wire.
    time_left = rps_match['match_expire_index'] - util.CURRENT_BLOCK_INDM
    if time_left < 4:
        logger.warning('Only {} blocks until that rps match expires. The conclusion might not make into the blockchain in time.'.format(time_left))
    tx0_hash_bytes = binascii.unhexlify(bytes(tx0_hash, 'utf-8'))
    tx1_hash_bytes = binascii.unhexlify(bytes(tx1_hash, 'utf-8'))
    random_bytes = binascii.unhexlify(bytes(random, 'utf-8'))
    data = message_type.pack(ID)
    data += struct.pack(FORMAT, move, random_bytes, tx0_hash_bytes, tx1_hash_bytes)
    return (source, [], data)
parse (db, tx, message):
    cursor = db.cursor()
    # Unpack message.
        if len(message) != LENGTH:
            raise exceptions.UnpackError
        move, random, tx0_hash_bytes, tx1_hash_bytes = struct.unpack(FORMAT, message)
        tx0_hash, tx1_hash = binascii.hexlify(tx0_hash_bytes).decode('utf-8'), binascii.hexlify(tx1_hash_bytes).decode('utf-8')
        rps_match_id = util.make_id(tx0_hash, tx1_hash)
        random = binascii.hexlify(random).decode('utf-8')
        status = 'valid'
t (exceptions.UnpackError, struct.error) as e:
        move, random, tx0_hash, tx1_hash, rps_match_id = None, None, None, None, None
        status = 'invalid: could not unpack'
    if status == 'valid':
        txn, rps_match, problems = validate(db, tx['source'], move, random, rps_match_id)
        if problems:
            rps_match = None
            status = 'invalid: ' + '; '.join(problems)
    # Add parsed transaction to message-type
    rpsresolves_bindings = {
        'tx_index': tx['tx_M
        'tx_hash': tx['tx_hash'],
        'block_index': tx['block_index'],
        'source': tx['source'],
        'move': move,
        'random': random,
        'rps_match_id': rps_match_id,
        'status': status
    if status == 'valid':
        rps_match_status = 'concluded'
        if rps_match['status'] == 'pending':
            rps_match_status = 'resolved and pending' if txn==0 else 'pending and resolved'
        if rps_match_status == 'concluded':
            counter_txn = 0 if txn ==M
            counter_source = rps_match['tx{}_address'.format(counter_txn)]
            sql = '''SELECT * FROM rpsresolves WHERE rps_match_id = ? AND source = ? AND status = ?'''
            counter_games = list(cursor.execute(sql, (rps_match_id, counter_source, 'valid')))
            assert len(counter_games) == 1
            counter_game = counter_games[0]
            winner = resolve_game(db, rpsresolves_bindings, counter_game)
            if winner == 0:
                rps_match_status = 'concluded:M
            elif winner == counter_game['tx_index']:
                rps_match_status = 'concluded: {} player wins'.format('first' if counter_txn == 0 else 'second')
                rps_match_status = 'concluded: {} player wins'.format('first' if txn == 0 else 'second')
        rps.update_rps_match_status(db, rps_match, rps_match_status, tx['block_index'])
    sql = '''INSERT INTO rpsresolves VALUES (:tx_index, :tx_hash, :block_index, :source, :move, :random, :rps_match_id, :status)'''
 cursor.execute(sql, rpsresolves_bindings)
# https://en.wikipedia.org/wiki/Rock-paper-scissors#Additional_weapons:
def resolve_game(db, resovlerps1, resovlerps2):
    move1 = resovlerps1['move']
    move2 = resovlerps2['move']
    same_parity = (move1 % 2) == (move2 % 2)
    if (same_parity and move1 < move2) or (not same_parity and move1 > move2):
        return resovlerps1['tx_index']
    elif (same_parity and move1 > move2) or (not same_parity and move1 < move2):
        return resovlerps2M
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
from counterpartylib.lib.messages.versions import send1
from counterpartylib.lib.messages.versions import enhanced_send
from counterpartylib.lib.messages.versions import mpma
from counterpartylib.lib import util
from counterpartylib.lib import exceptions
from counterpartylib.lib import config
def initialise (db):
    cursor = db.cursor()
    cursor.execute('''CM
REATE TABLE IF NOT EXISTS sends(
                      tx_index INTEGER PRIMARY KEY,
                      tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      source TEXT,
                      destination TEXT,
                      asset TEXT,
                      quantity INTEGER,
                      status TEXT,
                      FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
lumn['name'] for column in cursor.execute('''PRAGMA table_info(sends)''')]
    # If CIP10 activated, Create Sends copy, copy old data, drop old table, rename new table, recreate indexes
t do `ALTER TABLE IF COLUMN NOT EXISTS` nor can drop UNIQUE constraints
    if 'msg_index' not in columns:
        if 'memo' not in columns:
            cursor.execute('''CREATE TABLE IF NOT EXISTS new_sends(
                              tx_index INTEGER,
                              tx_hash TEXT,
                   block_index INTEGER,
                              source TEXT,
                              destination TEXT,
                              asset TEXT,
                              quantity INTEGER,
                              status TEXT,
                              msg_index INTEGER DEFAULT 0,
                              PRIMARY KEY (tx_index, msg_index),
                              FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index),
                              UNIQUE (tx_hash, msg_index) ON CONFLICT FAIL)
                           ''')
            cursor.execute('''INSERT INTO new_sends(tx_index, tx_hash, block_index, source, destination, asset, quantity, status)
                SELECT tx_index, tx_hash, block_index, source, destination, asset, quantity, status
                FROM sends''', {})
            cursor.execute('''CREATE TABLE IF NOT EXISTS new_sends(
                  tx_index INTEGER,
                  tx_hash TEXM
                  block_index INTEGER,
                  source TEXT,
                  destination TEXT,
                  asset TEXT,
                  quantity INTEGER,
                  status TEXT,
                  memo BLOB,
                  msg_index INTEGER DEFAULT 0,
                  PRIMARY KEY (tx_index, msg_index),
                  FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index),
                  UNIQUE (tx_hash, msg_index) ON CONFLICT FAIL)
            cursor.execute('''INSERT INTO new_sends (tx_index, tx_hash, block_index, source, destination, asset, quantity, status, memo)
                SELECT tx_index, tx_hash, block_index, source, destination, asset, quantity, status, memo
                FROM sends''', {})
        cursor.execute('DROP TABLE sends')
        cursor.execute('ALTER TABLE new_sends RENAME TO sends')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON sends (block_index)
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON sends (source)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      destination_idx ON sends (destination)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      asset_idx ON sends (asset)
                   ''')
    # Adds a memo to sends
t do `ALTER TABLE IF COLUMN NOT EXISTS`.
    if 'memo' not in columns:M
        cursor.execute('''ALTER TABLE sends ADD COLUMN memo BLOB''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      memo_idx ON sends (memo)
                   ''')
def unpack(db, message, block_index):
    return send1.unpack(db, message, block_index)
def validate (db, source, destination, asset, quantity, block_index):
    return send1.validate(db, source, destination, asset, quantity, block_index)
def compose (db, source, destination, asset, quantity, memo=None, memo_is_hex=False, usM
e_enhanced_send=None):
    # special case - enhanced_send replaces send by default when it is enabled
    #   but it can be explicitly disabled with an API parameter
    if util.enabled('enhanced_sends'):
        # Another special case, if destination, asset and quantity are arrays, it's an MPMA send
        if isinstance(destination, list) and isinstance(asset, list) and isinstance(quantity, list):
            if util.enabled('mpma_sends'):
                if len(destination) == len(asset) and len(asset) == len(quM
                    # Sending memos in a MPMA message can be done by several approaches:
                    # 1. Send a list of memos, there must be one for each send and they correspond to the sends by index
                    #   - In this case memo_is_hex should be a list with the same cardinality
                    # 2. Send a dict with the message specific memos and the message wide memo (same for the hex specifier):
                    #   - Each dict should have 2 members:
                    #  M
   + list: same as case (1). An array that specifies the memo for each send
                    #     + msg_wide: the memo for the whole message. This memo will be used for sends that don't have a memo specified. Same as in (3)
                    # 3. Send one memo (either bytes or string) and True/False in memo_is_hex. This will be interpreted as a message wide memo.
                    if (len(destination) > config.MPMA_LIMIT):
                        raise exceptions.ComposeError('mpma sends have a maximum of 'M
+str(config.MPMA_LIMIT)+' sends')

                    if isinstance(memo, list) and isinstance(memo_is_hex, list):
                        # (1) implemented here
                        if len(memo) != len(memo_is_hex):
                            raise exceptions.ComposeError('memo and memo_is_hex lists should have the same length')
                        elif len(memo) != len(destination):
                            raise exceptions.ComposeError('memo/memo_is_hex lists should have the same M
                        return mpma.compose(db, source, util.flat(zip(asset, destination, quantity, memo, memo_is_hex)), None, None)
                    elif isinstance(memo, dict) and isinstance(memo_is_hex, dict):
                        # (2) implemented here
                        if not('list' in memo and 'list' in memo_is_hex and 'msg_wide' in memo and 'msg_wide' in memo_is_hex):
                            raise exceptions.ComposeError('when specifying memo/memo_is_hex as a dict, they musM
t contain keys "list" and "msg_wide"')
                        elif len(memo['list']) != len(memo_is_hex['list']):
                            raise exceptions.ComposeError('length of memo.list and memo_is_hex.list must be equal')
                        elif len(memo['list']) != len(destination):
                            raise exceptions.ComposeError('length of memo.list/memo_is_hex.list must be equal to the amount of sends')
                        return mpma.compose(db, source, util.flat(zip(asset, destinatM
ion, quantity, memo['list'], memo_is_hex['list'])), memo['msg_wide'], memo_is_hex['msg_wide'])
                    else:
                        # (3) the default case
                        return mpma.compose(db, source, util.flat(zip(asset, destination, quantity)), memo, memo_is_hex)
                else:
                    raise exceptions.ComposeError('destination, asset and quantity arrays must have the same amount of elements')
                raise exceptions.ComposeError('mpma sends areM
        elif use_enhanced_send is None or use_enhanced_send == True:
            return enhanced_send.compose(db, source, destination, asset, quantity, memo, memo_is_hex)
    elif memo is not None or use_enhanced_send == True:
        raise exceptions.ComposeError('enhanced sends are not enabled')
    return send1.compose(db, source, destination, asset, quantity)
def parse (db, tx, message):    # TODO: *args
    return send1.parse(db, tx, message)
# vim: tabstop=8 expandtab shiftwidth=4 softtabstM
from counterpartylib.lib import exceptions
from counterpartylib.lib import config
from counterpartylib.lib import util
from counterpartylib.lib import log
from counterpartylib.lib import message_type
from counterpartylib.lib import address
from counterpartylib.lib.exceptions import *
MAX_MEMO_LENGTH = 34 # Could be higher, but we will keep it consistent with enhanced send
ANTISPAM_FEE_DECIMAL = 0.5
ANTISPAM_FEE_DECIMAL * config.UNIT
FLAG_BINARY_MEMO = 4
FLAGS_ALL = FLAG_BINARY_MEMO | FLAG_BALANCES | FLAG_OWNERSHIP
    cursor = db.cursor()
    cursor.execute('''CREATE TABLE IF NOT EXISTS sweeps(
                      tx_index INTEGER PRIMARY KEY,
                      tx_hash TEXT UNIQUE,
                      block_index INTEGER,
                      source TEXT,
                      destination TEXT,
                      flags INTEGER,
            status TEXT,
                      memo BLOB,
                      fee_paid INTEGER,
                      FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index))
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      block_index_idx ON sweeps (block_index)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      source_idx ON sweeps (source)
                   ''')
cute('''CREATE INDEX IF NOT EXISTS
                      destination_idx ON sweeps (destination)
                   ''')
    cursor.execute('''CREATE INDEX IF NOT EXISTS
                      memo_idx ON sweeps (memo)
                   ''')
def validate (db, source, destination, flags, memo, block_index):
    if source == destination:
        problems.append('destination cannot be the same as source')
    cursor = db.cursor()
    cursor.execute('''SELECT * FROM balances WHERE (address = ? ANDM
 asset = ?)''', (source, 'XCP'))
    result = cursor.fetchall()
    if len(result) == 0:
        problems.append('insufficient XCP balance for sweep. Need %s XCP for antispam fee' % ANTISPAM_FEE_DECIMAL)
    elif result[0]['quantity'] < ANTISPAM_FEE:
        problems.append('insufficient XCP balance for sweep. Need %s XCP for antispam fee' % ANTISPAM_FEE_DECIMAL)
    if flags > FLAGS_ALL:
        problems.append('invalid flags %i' % flags)
    elif not(flags & (FLAG_BALANCES | FLAG_OWNERSHIP))M
        problems.append('must specify which kind of transfer in flags')
    if memo and len(memo) > MAX_MEMO_LENGTH:
        problems.append('memo too long')
def compose (db, source, destination, flags, memo):
    if memo is None:
    elif flags & FLAG_BINARY_MEMO:
        memo = bytes.fromhex(memo)
        memo = memo.encode('utf-8')
        memo = struct.pack(">{}s".format(len(memo)), memo)
    block_index = util.CURRENT_BLOCK_INDEX
    problems = validate(dbM
, source, destination, flags, memo, block_index)
    if problems: raise exceptions.ComposeError(problems)
    short_address_bytes = address.pack(destination)
    data = message_type.pack(ID)
    data += struct.pack(FORMAT, short_address_bytes, flags)
    return (source, [], data)
def unpack(db, message, block_index):
        memo_bytes_length = len(message) - LENGTH
        if memo_bytes_length < 0:
            raise exceptions.UnpackError('invalid message length')
        if memo_byteM
s_length > MAX_MEMO_LENGTH:
            raise exceptions.UnpackError('memo too long')
        struct_format = FORMAT + ('{}s'.format(memo_bytes_length))
        short_address_bytes, flags, memo_bytes = struct.unpack(struct_format, message)
        if len(memo_bytes) == 0:
            memo_bytes = None
        elif not(flags & FLAG_BINARY_MEMO):
            memo_bytes = memo_bytes.decode('utf-8')
        # unpack address
        full_address = address.unpack(short_address_bytes)
    except (struct.error) as e:
     logger.warning("sweep send unpack error: {}".format(e))
        raise exceptions.UnpackError('could not unpack')
      'destination': full_address,
      'flags': flags,
      'memo': memo_bytes,
def parse (db, tx, message):
    cursor = db.cursor()
    fee_paid = round(config.UNIT/2)
    # Unpack message.
        unpacked = unpack(db, message, tx['block_index'])
        destination, flags, memo_bytes = unpacked['destination'], unpacked['flags'], unpackedM
        status = 'valid'
    except (exceptions.UnpackError, exceptions.AssetNameError, struct.error) as e:
        destination, flags, memo_bytes = None, None, None
        status = 'invalid: could not unpack ({})'.format(e)
    except BalanceError:
        destination, flags, memo_bytes = None, None, None
        status = 'invalid: insufficient balance for antispam fee for sweep'
    except Exception as err:
        destination, flags, memo_bytes = None, None, None
        status = 'invalid: could not uM
    if status == 'valid':
        problems = validate(db, tx['source'], destination, flags, memo_bytes, tx['block_index'])
        if problems: status = 'invalid: ' + '; '.join(problems)
    if status == 'valid':
            util.debit(db, tx['source'], 'XCP', fee_paid, action='sweep fee', event=tx['tx_hash'])
        except BalanceError:
            destination, flags, memo_bytes = None, None, None
            status = 'invalid: insufficient balance for antispam fee for sweep'
   if status == 'valid':
        cursor.execute('''SELECT * FROM balances WHERE address = ?''', (tx['source'],))
        balances = cursor.fetchall()
        if flags & FLAG_BALANCES:
            for balance in balances:
                util.debit(db, tx['source'], balance['asset'], balance['quantity'], action='sweep', event=tx['tx_hash'])
                util.credit(db, destination, balance['asset'], balance['quantity'], action='sweep', event=tx['tx_hash'])
        if flags & FLAG_OWNERSHIP:
            sweep_posM
            for balance in balances:
                cursor.execute('''SELECT * FROM issuances \
                                  WHERE (status = ? AND asset = ?)
                                  ORDER BY tx_index ASC''', ('valid', balance['asset']))
                issuances = cursor.fetchall()
                if len(issuances) > 0:
                    last_issuance = issuances[-1]
                    if last_issuance['issuer'] == tx['source']:
                        bindings= {
                           M
 'tx_index': tx['tx_index'],
                            'tx_hash': tx['tx_hash'],
                            'msg_index': sweep_pos,
                            'block_index': tx['block_index'],
                            'asset': balance['asset'],
                            'quantity': 0,
                            'divisible': last_issuance['divisible'],
                            'source': last_issuance['source'],
                            'issuer': destination,
                            'transfer': TrM
                            'callable': last_issuance['callable'],
                            'call_date': last_issuance['call_date'],
                            'call_price': last_issuance['call_price'],
                            'description': last_issuance['description'],
                            'fee_paid': 0,
                            'locked': last_issuance['locked'],
                            'status': status,
                            'asset_longname': last_issuance['asset_longname'],
                       'reset': False
                        }
                        sql='insert into issuances values(:tx_index, :tx_hash, :msg_index, :block_index, :asset, :quantity, :divisible, :source, :issuer, :transfer, :callable, :call_date, :call_price, :description, :fee_paid, :locked, :status, :asset_longname, :reset)'
                        cursor.execute(sql, bindings)
                        sweep_pos += 1
        bindings = {
            'tx_index': tx['tx_index'],
            'tx_hash': tx['tx_hM
            'block_index': tx['block_index'],
            'source': tx['source'],
            'destination': destination,
            'flags': flags,
            'status': status,
            'memo': memo_bytes,
            'fee_paid': fee_paid
        sql = 'insert into sweeps values(:tx_index, :tx_hash, :block_index, :source, :destination, :flags, :status, :memo, :fee_paid)'
        cursor.execute(sql, bindings)
# Each message gets identified by itsM
# The only exception is send.version1 which is 0 the first to fourth byte.
# Used IDs for messages:
# 1 send.enhanced_send
# 21 issuance.subasset
# Allocate each new type of message within the "logical" 10 number boundary
# Only allocate a 10 number boundary if it makes sense
#### enhanced_send.py
logger = logging.getLogger(__name__)
from counterpartylib.lib import (config, util, exceptions, util, message_type, address)
MAX_MEMO_LENGTH = 34
def unpack(db, message, block_index):
        # account for memo bytes
        memo_bytes_length = len(message) - LENGTH
        if memo_bytes_length < 0:
            raise exceptions.UnpackError('invalid message length')
        if memo_bytes_length > MAX_MEMO_LENGTH:
            raise exceptions.UnpackError('memo too long')
        struct_format = FORMAT + ('{}s'.format(memo_bytes_length))
        asset_id, quantity, short_address_bytes, memo_bytes = struct.unpack(struct_format, message)
        if len(memo_bytes) == 0:
            memo_bytes = None
        # unpack address
        full_address = address.unpack(short_address_bytes)
        # asset id to name
        asset = util.generate_asset_name(asset_id, block_index)
        if asset == config.BTC:
            raise exceM
ptions.AssetNameError('{} not allowed'.format(config.BTC))
    except (struct.error) as e:
        logger.warning("enhanced send unpack error: {}".format(e))
        raise exceptions.UnpackError('could not unpack')
    except (exceptions.AssetNameError, exceptions.AssetIDError) as e:
        logger.warning("enhanced send invalid asset id: {}".format(e))
        raise exceptions.UnpackError('asset id invalid')
      'asset': asset,
      'quantity': quantity,
      'address': full_address,
def validate (db, source, destination, asset, quantity, memo_bytes, block_index):
    if asset == config.BTC: problems.append('cannot send {}'.format(config.BTC))
    if not isinstance(quantity, int):
        problems.append('quantity must be in satoshis')
        return problems
    if quantity < 0:
        problems.append('negative quantity')
    if quantity == 0:
        problems.append('zero quantity')
    if quantity > confM
        problems.append('integer overflow')
    # destination is always required
    if not destination:
        problems.append('destination is required')
    if memo_bytes is not None and len(memo_bytes) > MAX_MEMO_LENGTH:
      problems.append('memo is too long')
    if util.enabled('options_require_memo'):
        cursor = db.cursor()
            results = cursor.execute('SELECT options FROM addresses WHERE address=?', (destination,))
            if results:
        result = results.fetchone()
                if result and util.active_options(result['options'], config.ADDRESS_OPTION_REQUIRE_MEMO):
                    if memo_bytes is None or (len(memo_bytes) == 0):
                        problems.append('destination requires memo')
            cursor.close()
def compose (db, source, destination, asset, quantity, memo, memo_is_hex):
    cursor = db.cursor()
    # Just send BTC?
    if asset == config.BTC:
        return (source, M
[(destination, quantity)], None)
    # resolve subassets
    asset = util.resolve_subasset_longname(db, asset)
    #quantity must be in int satoshi (not float, string, etc)
    if not isinstance(quantity, int):
        raise exceptions.ComposeError('quantity must be an int (in satoshi)')
    # Only for outgoing (incoming will overburn).
    balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, asset)))
    if not balances or balances[0]['quantity'] < quantity:
      raise exceptions.ComposeError('insufficient funds')
    # convert memo to memo_bytes based on memo_is_hex setting
    if memo is None:
        memo_bytes = b''
    elif memo_is_hex:
        memo_bytes = bytes.fromhex(memo)
        memo = memo.encode('utf-8')
        memo_bytes = struct.pack(">{}s".format(len(memo)), memo)
    block_index = util.CURRENT_BLOCK_INDEX
    problems = validate(db, source, destination, asset, quantity, memo_bytes, block_index)
    if problems: raise exceptions.ComposeErM
    asset_id = util.get_asset_id(db, asset, block_index)
    short_address_bytes = address.pack(destination)
    data = message_type.pack(ID)
    data += struct.pack(FORMAT, asset_id, quantity, short_address_bytes)
    data += memo_bytes
    # return an empty array as the second argument because we don't need to send BTC dust to the recipient
    return (source, [], data)
def parse (db, tx, message):
    cursor = db.cursor()
    # Unpack message.
        unpacked = unM
pack(db, message, tx['block_index'])
        asset, quantity, destination, memo_bytes = unpacked['asset'], unpacked['quantity'], unpacked['address'], unpacked['memo']
        status = 'valid'
    except (exceptions.UnpackError, exceptions.AssetNameError, struct.error) as e:
        asset, quantity, destination, memo_bytes = None, None, None, None
        status = 'invalid: could not unpack ({})'.format(e)
        asset, quantity, destination, memo_bytes = None, None, None, None
        status = 'invalM
id: could not unpack'
    if status == 'valid':
        # don't allow sends over MAX_INT at all
        if quantity and quantity > config.MAX_INT:
            status = 'invalid: quantity is too large'
            quantity = None
    if status == 'valid':
        problems = validate(db, tx['source'], destination, asset, quantity, memo_bytes, tx['block_index'])
        if problems: status = 'invalid: ' + '; '.join(problems)
    if status == 'valid':
        # verify balance is present
        cursor.execute('''SELM
ECT * FROM balances \
                                     WHERE (address = ? AND asset = ?)''', (tx['source'], asset))
        balances = cursor.fetchall()
        if not balances or balances[0]['quantity'] < quantity:
            status = 'invalid: insufficient funds'
    if status == 'valid':
        util.debit(db, tx['source'], asset, quantity, action='send', event=tx['tx_hash'])
        util.credit(db, destination, asset, quantity, action='send', event=tx['tx_hash'])
    # log invalid transactions
        if quantity is None:
            logger.warn("Invalid send from %s with status %s. (%s)" % (tx['source'], status, tx['tx_hash']))
            logger.warn("Invalid send of %s %s from %s to %s. status is %s. (%s)" % (quantity, asset, tx['source'], destination, status, tx['tx_hash']))
    # Add parsed transaction to message-type
        'tx_index': tx['tx_index'],
        'tx_hash': tx['tx_hash'],
        'block_index': tx['block_index'],
   'source': tx['source'],
        'destination': destination,
        'asset': asset,
        'quantity': quantity,
        'status': status,
        'memo': memo_bytes,
    if "integer overflow" not in status and "quantity must be in satoshis" not in status:
        sql = 'insert into sends (tx_index, tx_hash, block_index, source, destination, asset, quantity, status, memo) values(:tx_index, :tx_hash, :block_index, :source, :destination, :asset, :quantity, :status, :memo)'
        cursor.execute(sql, bindinM
        logger.warn("Not storing [send] tx [%s]: %s" % (tx['tx_hash'], status))
        logger.debug("Bindings: %s" % (json.dumps(bindings), ))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
from bitcoin.core import key
from functools import reduce
from itertools import groupby
logger = logging.getLogger(__name__)
from bitstring import ReadError
artylib.lib import (config, util, exceptions, util, message_type, address)
from .mpma_util.internals import (_decode_mpmaSendDecode, _encode_mpmaSend)
ID = 3 # 0x03 is this specific message type
## expected functions for message version
def unpack(db, message, block_index):
        unpacked = _decode_mpmaSendDecode(message, block_index)
    except (struct.error) as e:
        raise exceptions.UnpackError('could not unpack')
    except (exceptions.AssetNameError, exceptions.AssetIDError) as e:
ise exceptions.UnpackError('invalid asset in mpma send')
    except (ReadError) as e:
        raise exceptions.UnpackError('truncated data')
def validate (db, source, asset_dest_quant_list, block_index):
    if len(asset_dest_quant_list) == 0:
        problems.append('send list cannot be empty')
    if len(asset_dest_quant_list) == 1:
        problems.append('send list cannot have only one element')
    if len(asset_dest_quant_list) > 0:
        # Need to manually unpack tM
he tuple to avoid errors on scenarios where no memo is specified
        grpd = groupby([(t[0], t[1]) for t in asset_dest_quant_list])
        lengrps = [len(list(grpr)) for (group, grpr) in grpd]
        cardinality = max(lengrps)
        if cardinality > 1:
            problems.append('cannot specify more than once a destination per asset')
    cursor = db.cursor()
    for t in asset_dest_quant_list:
        # Need to manually unpack the tuple to avoid errors on scenarios where no memo is specified
        destination = t[1]
        quantity = t[2]
        sendMemo = None
        if len(t) > 3:
            sendMemo = t[3]
        if asset == config.BTC: problems.append('cannot send {} to {}'.format(config.BTC, destination))
        if not isinstance(quantity, int):
            problems.append('quantities must be an int (in satoshis) for {} to {}'.format(asset, destination))
        if quantity < 0:
            problems.append('negative quantity for {} to {}'.format(asset, destination))
            problems.append('zero quantity for {} to {}'.format(asset, destination))
        # For SQLite3
        if quantity > config.MAX_INT:
            problems.append('integer overflow for {} to {}'.format(asset, destination))
        # destination is always required
        if not destination:
            problems.append('destination is required for {}'.format(asset))
        if util.enabled('options_require_memo'):
            results = cursor.execute('SELECT options FROM addresses WHERE M
address=?', (destination,))
            if results:
                result = results.fetchone()
                if result and result['options'] & config.ADDRESS_OPTION_REQUIRE_MEMO and (sendMemo is None):
                    problems.append('destination {} requires memo'.format(destination))
def compose (db, source, asset_dest_quant_list, memo, memo_is_hex):
    cursor = db.cursor()
    out_balances = util.accumulate([(t[0], t[2]) for t in asset_dest_quant_list])
asset, quantity) in out_balances:
        if util.enabled('mpma_subasset_support'):
            # resolve subassets
            asset = util.resolve_subasset_longname(db, asset)
        if not isinstance(quantity, int):
            raise exceptions.ComposeError('quantities must be an int (in satoshis) for {}'.format(asset))
        balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, asset)))
        if not balances or balances[0]['quantity'] < quantity:
    raise exceptions.ComposeError('insufficient funds for {}'.format(asset))
    block_index = util.CURRENT_BLOCK_INDEX
    problems = validate(db, source, asset_dest_quant_list, block_index)
    if problems: raise exceptions.ComposeError(problems)
    data = message_type.pack(ID)
    data += _encode_mpmaSend(db, asset_dest_quant_list, block_index, memo=memo, memo_is_hex=memo_is_hex)
    return (source, [], data)
def parse (db, tx, message):
        unpacked = unpack(db, message, tM
        status = 'valid'
    except (struct.error) as e:
        status = 'invalid: truncated message'
    except (exceptions.AssetNameError, exceptions.AssetIDError) as e:
        status = 'invalid: invalid asset name/id'
    except (Exception) as e:
        status = 'invalid: couldn\'t unpack; %s' % e
    cursor = db.cursor()
    plain_sends = []
    all_credits = []
    if status == 'valid':
        for asset_id in unpacked:
                asset = util.geM
t_asset_name(db, asset_id, tx['block_index'])
            except (exceptions.AssetNameError) as e:
                status = 'invalid: asset %s invalid at block index %i' % (asset_id, tx['block_index'])
                break
            cursor.execute('''SELECT * FROM balances \
                              WHERE (address = ? AND asset = ?)''', (tx['source'], asset_id))
            balances = cursor.fetchall()
            if not balances:
                status = 'invalid: insufficient funds for asset %s, addressM
 %s has no balance' % (asset_id, tx['source'])
                break
            credits = unpacked[asset_id]
            total_sent = reduce(lambda p, t: p + t[1], credits, 0)
            if balances[0]['quantity'] < total_sent:
                status = 'invalid: insufficient funds for asset %s, needs %i' % (asset_id, total_sent)
                break
            if status == 'valid':
                plain_sends += map(lambda t: util.py34TupleAppend(asset_id, t), credits)
                all_credits += map(lamM
bda t: {"asset": asset_id, "destination": t[0], "quantity": t[1]}, credits)
                all_debits.append({"asset": asset_id, "quantity": total_sent})
    if status == 'valid':
        problems = validate(db, tx['source'], plain_sends, tx['block_index'])
        if problems: status = 'invalid:' + '; '.join(problems)
    if status == 'valid':
        for op in all_credits:
            util.credit(db, op['destination'], op['asset'], op['quantity'], action='mpma send', event=tx['tx_hash'])
        for op in alM
            util.debit(db, tx['source'], op['asset'], op['quantity'], action='mpma send', event=tx['tx_hash'])
        # Enumeration of the plain sends needs to be deterministic, so we sort them by asset and then by address
        plain_sends = sorted(plain_sends, key=lambda x: ''.join([x[0], x[1]]))
        for i, op in enumerate(plain_sends):
            if len(op) > 3:
                memo_bytes = op[3]
                memo_bytes = None
            bindings = {
                'tx_M
index': tx['tx_index'],
                'tx_hash': tx['tx_hash'],
                'block_index': tx['block_index'],
                'source': tx['source'],
                'asset': op[0],
                'destination': op[1],
                'quantity': op[2],
                'status': status,
                'memo': memo_bytes,
                'msg_index': i
            sql = 'insert into sends (tx_index, tx_hash, block_index, source, destination, asset, quantity, status, memo, msg_index) values(:txM
_index, :tx_hash, :block_index, :source, :destination, :asset, :quantity, :status, :memo, :msg_index)'
            cursor.execute(sql, bindings)
    if status != 'valid':
        logger.warn("Not storing [mpma] tx [%s]: %s" % (tx['tx_hash'], status))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
"""Create and parse 'send'-type messages."""
logger = logging.getLogger(__name__)
from ... import (confiM
g, exceptions, util, message_type)
def unpack(db, message, block_index):
    # Only used for `unpack` API call at the moment.
        asset_id, quantity = struct.unpack(FORMAT, message)
        asset = util.get_asset_name(db, asset_id, block_index)
    except struct.error:
        raise exceptions.UnpackError('could not unpack')
    except AssetNameError:
        raise exceptions.UnpackError('asset id invalid')
                'asset': asset,
           'quantity': quantity
def validate (db, source, destination, asset, quantity, block_index):
    if asset == config.BTC: problems.append('cannot send bitcoins')  # Only for parsing.
    if not isinstance(quantity, int):
        problems.append('quantity must be in satoshis')
        return problems
    if quantity < 0:
        problems.append('negative quantity')
    if quantity > config.MAX_INT:
        problems.append('integer M
    if util.enabled('send_destination_required'):  # Protocol change.
        if not destination:
            problems.append('destination is required')
    if util.enabled('options_require_memo'):
        # Check destination address options
        cursor = db.cursor()
        results = cursor.execute('SELECT options FROM addresses WHERE address=?', (destination,))
            result = results.fetchone()
            if result and util.active_options(result['options'], config.ADDREM
SS_OPTION_REQUIRE_MEMO):
                problems.append('destination requires memo')
        cursor.close()
def compose (db, source, destination, asset, quantity):
    cursor = db.cursor()
    # Just send BTC?
    if asset == config.BTC:
        return (source, [(destination, quantity)], None)
    # resolve subassets
    asset = util.resolve_subasset_longname(db, asset)
    #quantity must be in int satoshi (not float, string, etc)
    if not isinstance(quantity, int):
        raise exceptiM
ons.ComposeError('quantity must be an int (in satoshi)')
    # Only for outgoing (incoming will overburn).
    balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, asset)))
    if not balances or balances[0]['quantity'] < quantity:
        raise exceptions.ComposeError('insufficient funds')
    block_index = util.CURRENT_BLOCK_INDEX
    problems = validate(db, source, destination, asset, quantity, block_index)
    if problems: raise exceptions.ComposeError(problM
    asset_id = util.get_asset_id(db, asset, block_index)
    data = message_type.pack(ID)
    data += struct.pack(FORMAT, asset_id, quantity)
    return (source, [(destination, None)], data)
def parse (db, tx, message):
    cursor = db.cursor()
    # Unpack message.
        if len(message) != LENGTH:
            raise exceptions.UnpackError
        asset_id, quantity = struct.unpack(FORMAT, message)
        asset = util.get_asset_name(db, asset_id, tx['block_index'])
    except (exceptions.UnpackError, exceptions.AssetNameError, struct.error) as e:
        asset, quantity = None, None
        status = 'invalid: could not unpack'
    if status == 'valid':
        cursor.execute('''SELECT * FROM balances \
                                     WHERE (address = ? AND asset = ?)''', (tx['source'], asset))
        balances = cursor.fetchall()
        if not balances:
            status = 'invalid: insufficient funds'
        elif balances[0]['quantitM
            quantity = min(balances[0]['quantity'], quantity)
        quantity = min(quantity, config.MAX_INT)
    if status == 'valid':
        problems = validate(db, tx['source'], tx['destination'], asset, quantity, tx['block_index'])
        if problems: status = 'invalid: ' + '; '.join(problems)
    if status == 'valid':
        util.debit(db, tx['source'], asset, quantity, action='send', event=tx['tx_hash'])
        util.credit(db, tx['destination'], assetM
, quantity, action='send', event=tx['tx_hash'])
    # Add parsed transaction to message-type
        'tx_index': tx['tx_index'],
        'tx_hash': tx['tx_hash'],
        'block_index': tx['block_index'],
        'source': tx['source'],
        'destination': tx['destination'],
        'asset': asset,
        'quantity': quantity,
        'status': status,
    if "integer overflow" not in status and "quantity must be in satoshis" not in status:
        sql = 'insert into seM
nds (tx_index, tx_hash, block_index, source, destination, asset, quantity, status, memo) values(:tx_index, :tx_hash, :block_index, :source, :destination, :asset, :quantity, :status, NULL)'
        cursor.execute(sql, bindings)
        logger.warn("Not storing [send] tx [%s]: %s" % (tx['tx_hash'], status))
        logger.debug("Bindings: %s" % (json.dumps(bindings), ))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
from bitcoin.core import key
from functools import reduce
from itertools import groupby
from bitstring import BitArray, BitStream, ConstBitStream, ReadError
logger = logging.getLogger(__name__)
from counterpartylib.lib import (config, util, exceptions, util, message_type, address)
## encoding functions
def _encode_constructBaseLUT(snds):
    # t is a tuple of the form (asset, addr, amnt [, memo, is_hex])
    return sorted(list(set([t[1] for t in snM
ds]))) # Sorted to make list determinist
def _encode_constructBaseAssets(sends):
    # t is a tuple of the form (asset, addr, amnt [, memo, is_hex])
    return sorted(list(set([t[0] for t in sends]))) # Sorted to make list determinist
def _encode_constructLUT(sends):
    baseLUT = _encode_constructBaseLUT(sends)
    # What's this? It calculates the minimal number of bits needed to represent an item index inside the baseLUT
    lutNbits = math.ceil(math.log2(len(baseLUT)))
        "nbits": lutNbits,M
        "addrs": baseLUT
def _encode_compressLUT(lut):
    return b''.join([struct.pack('>H', len(lut['addrs']))] +
            address.pack(addr)
            for addr in lut['addrs']
def _encode_memo(memo=None, is_hex=False):
    '''Tightly pack a memo as a Bit array'''
    if memo is not None:
        # signal a 1 bit for existence of the memo
        barr = BitArray('0b1')
            # signal a 1 bit for hex encoded memos
            barr.append('0b1')
    if type(memo) is str: # append the string as hex-string
                barr.append('uint:6=%i' % (len(memo) >> 1))
                memo = '0x%s' % memo
                barr.append('uint:6=%i' % len(memo))
            barr.append(memo)
            # signal a 0 bit for a string encoded memo
            barr.append('0b0')
            barr.append('uint:6=%i' % len(memo))
            barr.append(BitArray(memo.encode('utf-8')))
        # if the memo is M
None, return just a 0 bit
        return BitArray('0b0')
def _safe_tuple_index(t, i):
    '''Get an element from a tuple, returning None if it's out of bounds'''
def _encode_constructSendList(send_asset, lut, sends):
    # t is a tuple of the form (asset, addr, amnt, memo, is_hex)
    # if there's no memo specified, memo and is_hex are None
        (lut['addrs'].index(t[1]), t[2], _safe_tuple_index(t, 3), _safe_tuple_index(t, 4))
        for t in sends
        if t[0] == send_asset
def _solve_asset(db, assetName, block_index):
    asset = util.resolve_subasset_longname(db, assetName)
    return util.get_asset_id(db, asset, block_index)
def _encode_compressSendList(db, nbits, send, block_index):
    r.append('uintbe:64=%i' % _solve_asset(db, send['assetName'], block_index))
    r.append('uint:%i=%i' % (nbits, len(send['sendList'])-1))
    for sendItem in send['sendList']:
        idx = sendItem[0]
        r.append('uint:%i=%i' % (nbits, idx))
        r.append('uintbe:64=%i' % amnt)
            memoStr = _encode_memo(memo=sendItem[2], is_hex=sendItem[3])
            memoStr = BitArray('0b0')
        r.append(memoStr)
def _encode_constructSends(sends):
    lut = _encode_constructLUT(sends)
    assets = _encode_constructBaseAssets(sends)
            "assetName": asset,
            "sendList": _encode_constructSendList(asset, lutM
        for asset in assets
        "sendLists": sendLists
def _encode_compressSends(db, mpmaSend, block_index, memo=None, memo_is_hex=False):
    compressedLUT = _encode_compressLUT(mpmaSend['lut'])
    memo_arr = _encode_memo(memo, memo_is_hex).bin
    isends = '0b' + memo_arr + ''.join([
        ''.join(['1', _encode_compressSendList(db, mpmaSend['lut']['nbits'], sendList, block_index).bin])
        for sendList in mpmaSend['sendLists']
  bstr = ''.join([isends, '0'])
    pad = '0' * ((8 - (len(bstr) - 2)) % 8) # That -2 is because the prefix 0b is there
    barr = BitArray(bstr + pad)
    return b''.join([
        compressedLUT,
def _encode_mpmaSend(db, sends, block_index, memo=None, memo_is_hex=False):
    mpma = _encode_constructSends(sends)
    send = _encode_compressSends(db, mpma, block_index, memo=memo, memo_is_hex=memo_is_hex)
## decoding functions
def _decode_decodeLUT(data):
,) = struct.unpack('>H', data[0:2])
    if numAddresses == 0:
        raise exceptions.DecodeError('address list can\'t be empty')
    addressList = []
    bytesPerAddress = 21
    for i in range(0, numAddresses):
        addr_raw = data[p:p+bytesPerAddress]
        addressList.append(address.unpack(addr_raw))
        p += bytesPerAddress
    lutNbits = math.ceil(math.log2(numAddresses))
    return addressList, lutNbits, data[p:]
def _decode_decodeSendList(stream, nbits, lut, block_index):
id = stream.read('uintbe:64')
        numRecipients = stream.read('uint:%i' % nbits)
        rangeLimit = numRecipients + 1
        numRecipients = 1
        rangeLimit = numRecipients
    asset = util.generate_asset_name(asset_id, block_index)
    for i in range(0, rangeLimit):
        if nbits > 0:
            idx = stream.read('uint:%i' % nbits)
        addr = lut[idx]
        amount = stream.read('uintbe:64')
        memo, is_hex M
= _decode_memo(stream)
        if memo is not None:
            sendList.append((addr, amount, memo, is_hex))
            sendList.append((addr, amount))
    return asset, sendList
def _decode_decodeSends(stream, nbits, lut, block_index):
    #stream = ConstBitStream(data)
    while stream.read('bool'):
        asset, sendList = _decode_decodeSendList(stream, nbits, lut, block_index)
        sends[asset] = sendList
def _decode_memo(stream):
    if stream.read('booM
        is_hex = stream.read('bool')
        mlen = stream.read('uint:6')
        data = stream.read('bytes:%i' % mlen)
        if not(is_hex):
            # is an utf8 string
            data = data.decode('utf-8')
        return data, is_hex
        return None, None
def _decode_mpmaSendDecode(data, block_index):
    lut, nbits, remain = _decode_decodeLUT(data)
    stream = ConstBitStream(remain)
    memo, is_hex = _decode_memo(stream)
    sends = _decode_decodeSends(stream, nbits, lut, block_inM,
    if memo is not None:
        for asset in sends:
            sendList = sends[asset]
            for idx, send in enumerate(sendList):
                if len(send) == 2:
                    sendList[idx] = (send[0], send[1], memo, is_hex)
AUUUUUUUUUUUHUUUUUUUUUUUUUUUUUUi
!22222222222222222222222222222222222222222222222222
TUUUUUUUUUUUUUUUUUUUUUUUUUUUU
FPUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
text/plain;charset=utf-8
SjLPQuesto dovrebbe essere il limite per gli 80 caratteri di un testo op_return ciao
FjDOUT:2C73338A350D8C3395F29B96DB61A2295FC00D2FA324697D20F40623CBF42E54
 3e48013a564f5d725e88da2709abb6c7G0D
 4ae201a374e28e0b41697e7a5cc8a9baH0E
()*89:HIJWXYZghijwxyz
()*789:FGHIJUVWXYZdefghijstuvwxyz
c/Foundry USA Pool #dropgold/
EjCs:ETH.ETH:0x18A99CF66D9d80837Ee21734fA5C3d4D4686bb1A:261054731:ss:0
DjB=:ETH.ETH:0x6b749E0134288737dfc4827ba6D90A54ab7346F6:12723469:te:0
IjGREFUND:AB61703B275F8235DF30734970CB8FDA87E5CF161D6CF5E7F56A4BC5159ADFE6
Copyright Apple Inc., 2023
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
)4)))))4>444444>>>>>>>>KKKKKKWWWWWbbbbbbbbbb
+fE9Effffffffffffffffffffffffffffffffffffffffffffffffff
http://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 9.0-c000 79.171c27fab, 2022/08/16-22:35:41        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:OriginalDocumentID="xmpM
.did:8187f80b-58da-b441-beb6-f4facd64d755" xmpMM:DocumentID="xmp.did:49D755B5A32D11ED9141BCEBB78737A9" xmpMM:InstanceID="xmp.iid:49D755B4A32D11ED9141BCEBB78737A9" xmp:CreatorTool="Adobe Photoshop 24.1 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:cee66003-89ea-6e42-8659-2c4688cd6775" stRef:documentID="xmp.did:8187f80b-58da-b441-beb6-f4facd64d755"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>
 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
This is better than a JPG
MjK=:BNB.BUSD-BD1:bnb1aaahj4ye2rgavhqfm9cwgzem2tcs9xpt2f2n94:466681064925:t:30]
Bj@=:ETH.ETH:0x6eb4978c5d07379Bc7759F21f30B2b53EDBA0491:455695:te:0
Ahttp://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.5.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" photoshop:ColorMode="3" photoshop:ICCProfile="M
sRGB IEC61966-2.1" xmp:ModifyDate="2023-02-02T17:30:32-05:00" xmp:MetadataDate="2023-02-02T17:30:32-05:00"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="produced" stEvt:softwareAgent="Affinity Photo 1.10.6" stEvt:when="2023-02-02T17:30:32-05:00"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                                       M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                  <?xpacket end="w"?>
+?7BA>7<;ENcTEI^K;<VvW^gjopoCSz
3kG<Gkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
Ahttp://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.5.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" photoshop:ColorMode="3" photoshop:M
ICCProfile="sRGB IEC61966-2.1" xmp:ModifyDate="2023-02-02T17:30:06-05:00" xmp:MetadataDate="2023-02-02T17:30:06-05:00"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="produced" stEvt:softwareAgent="Affinity Photo 1.10.6" stEvt:when="2023-02-02T17:30:06-05:00"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                           M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                              <?xpacket end="w"?>
+?7BA>7<;ENcTEI^K;<VvW^gjopoCM
3kG<Gkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
c/SBICrypto.com Pool/
MjK=:BNB.BUSD-BD1:bnb1aaahj4ye2rgavhqfm9cwgzem2tcs9xpt2f2n94:467933815166:t:30
SjLPs:ETH.USDC-3606EB48:0x18A99CF66D9d80837Ee21734fA5C3d4D4686bb1A:214007541697:ss:0
9j7+:BTC/BTC::bc1q3f787hr38pmal87yxtpq8tng09q60ljjqqd759:0V
Aj?=:BNB.BNB:bnb1em5ake82afp2u6xtrnl9x879689gra3ezku0cz:17364:te:0
Aj?=:ETH.ETH:0x6eb4978c5d07379Bc7759F21f30B2b53EDBA0491:39697:te:0
CjA=:BNB.BNB:bnb15cy0flkvuhuvsjpsp0tphsauprxguuzj8ekuqv:2782480:te:0
EjC=:BNB.BTCB-1DE:bnb1l65zmzsteq2swtncp74f3etcmmsjfsqu7hdrkh:7482:te:0
IjGREFUND:CEEED9FCC85F8260DF2604F5C4DD4F7F7582602EEA9E2880D11B1E5E0E4A5278
FjDOUT:7FED7BBEDB95F8D99A6E191E785B3DCAAF3054152FDC6944CF952C2BD6090EE7
FjDOUT:21FECF0DFDDCE4C43C593F46B9C53203B3B81A5A0200F648C2A60E1FD2D9F237
FjDOUT:9D1038CC280A43152E0414C3BC25CD7B000DFA4CA3C336A8A1E27DCA8C2F6EC5
IjGREFUND:04BB70BADD38ACFB6DB78A85928267CB76D9F38DE42C25E1F10F9EC37C6C38A7
Bj@=:ETH.ETH:0x2E3E96d59d3052A1B73B579f777c5241441A9E93:194320:te:0
text/plain;charset=utf-8
INDUSTRIAL SOCIETY AND ITS FUTURE
1. The Industrial Revolution and its consequences have been a disaster for the
   human race. They have greatly increased the life-expectancy of those of us
 countries, but they have destabilized society, have
   made life unfulfilling, have subjected human beings to indignities, have led
   to widespread psychological suffering (in the Third World to physical
   suffering as well) and have inflicted severe damage on the natural worlM
   continued development of technology will worsen the situation. It will
   certainly subject human beings to greater indignities and inflict greater
   damage on the natural world, it will probably lead to greater social
   disruption and psychological suffering, and it may lead to increased physical
   suffering even in
2. The industrial-technological system may survive or it may break down. If it
   survives, it MAY eventually achieve a low level of physical and psychologM
   suffering, but only after passing through a long and very painful period of
   adjustment and only at the cost of permanently reducing human beings and many
   other living organisms to engineered products and mere cogs in the social
   machine. Furthermore, if the system survives, the consequences will be
   inevitable: There is no way of reforming or modifying the system so as to
   prevent it from depriving people of dignity and autonomy.
3. If the system breaks down the consequences will still beM
   the bigger the system grows the more disastrous the results of its breakdown
   will be, so if it is to break down it had best break down sooner rather than
4. We therefore advocate a revolution against the industrial system. This
   revolution may or may not make use of violence; it may be sudden or it may be
   a relatively gradual process spanning a few decades. We can
   that. But we do outline in a very general way the measures that those who
he industrial system should take in order to prepare the way for
   a revolution against that form of society. This is not to be a POLITICAL
   revolution. Its object will be to overthrow not governments but the economic
   and technological basis of the present society.
5. In this article we give attention to only some of the negative developments
   that have grown out of the industrial-technological system. Other such
   developments we mention only briefly or ignore altogether. This does not mean
 we regard these other developments as unimportant. For practical reasons
   we have to confine our discussion to areas that have received insufficient
   public attention or in which we have something new to say. For example, since
   there are well-developed environmental and wilderness movements, we have
   written very little about environmental degradation or the destruction of
   wild nature, even though we consider these to be highly important.
THE PSYCHOLOGY OF MODERN LEFTISM
6. Almost everyone will M
agree that we live in a deeply troubled society. One of
   the most widespread manifestations of the craziness of our world is leftism,
   so a discussion of the psychology of leftism can serve as an introduction to
   the discussion of the problems of modern society in general.
7. But what is leftism? During the first half of the 20th century leftism could
   have been practically identified with socialism. Today the movement is
   fragmented and it is not clear who can properly be called a leftist. When we M
   speak of leftists in this article we have in mind mainly socialists,
 types, feminists, gay and disability
   activists, animal rights activists and the like. But not everyone who is
   associated with one of these movements is a leftist. What we are trying to
   get at in discussing leftism is not so much movement or an ideology as
   a psychological type, or rather a collection of related types. Thus, what we
 will emerge more clearly inM
 the course of our discussion of
   leftist psychology. (Also, see paragraphs 227-230.)
8. Even so, our conception of leftism will remain a good deal less clear than we
   would wish, but there doesn
t seem to be any remedy for this. All we are
   trying to do here is indicate in a rough and approximate way the two
   psychological tendencies that we believe are the main driving force of modern
   leftism. We by no means claim to be telling the WHOLE truth about leftist
   psychology. Also, our discussion M
is meant to apply to modern leftism only. We
   leave open the question of the extent to which our discussion could be
   applied to the leftists of the 19th and early 20th centuries.
9. The two psychological tendencies that underlie modern leftism we call
feelings of inferiority
 Feelings of inferiority
   are characteristic of modern leftism as a whole, while oversocialization is
   characteristic only of a certain segment of modern leftism; but this segment
FEELINGS OF INFERIORITY
feelings of inferiority
 we mean not only inferiority feelings in the
    strict sense but a whole spectrum of related traits; low self-esteem,
    feelings of powerlessness, depressive tendencies, defeatism, guilt, self-
    hatred, etc. We argue that modern leftists tend to have some such feelings
    (possibly more or less repressed) and that these feelings are decisive in
    determining the direction of modern leftism.
11. When someone interprets as deM
rogatory almost anything that is said about him
    (or about groups with whom he identifies) we conclude that he has
    inferiority feelings or low self-esteem. This tendency is pronounced among
    minority rights activists, whether or not they belong to the minority groups
    whose rights they defend. They are hypersensitive about the words used to
    designate minorities and about anything that is said concerning minorities.
    Asian, a disabled person or a woman originally had no derogatory
 were merely the feminine equivalents of
 The negative connotations have been attached to
    these terms by the activists themselves. Some animal rights activists have
    gone so far as to reject the word
 and insist on its replacement by
 Leftish anthropologists go to great lengths to avoid
 anything about primitive peoples that could conceivably be
    interpreted as negative. They want to replace the world
 They seem almost paranoid about anything that might suggest
    that any primitive culture is inferior to our own. (We do not mean to imply
    that primitive cultures ARE inferior to ours. We merely point out the
    hypersensitivity of leftish anthropologists.)
12. Those who are most sensitive about
politically incorrect
not the average black ghetto- dweller, Asian immigrant, abused woman or
    disabled person, but a minority of activists, many of whom do not even
 group but come from privileged strata of society.
    Political correctness has its stronghold among university professors, who
    have secure employment with comfortable salaries, and the majority of whom
    are heterosexual white males from middle- to upper-middle-class families.
13. Many leftists have an intense identificationM
 with the problems of groups
    that have an image of being weak (women), defeated (American Indians),
    repellent (homosexuals) or otherwise inferior. The leftists themselves feel
    that these groups are inferior. They would never admit to themselves that
    they have such feelings, but it is precisely because they do see these
    groups as inferior that they identify with their problems. (We do not mean
    to suggest that women, Indians, etc. ARE inferior; we are only making
    a point about leftiM
14. Feminists are desperately anxious to prove that women are as strong and as
    capable as men. Clearly they are nagged by a fear that women may NOT be as
    strong and as capable as men.
15. Leftists tend to hate anything that has an image of being strong, good and
    successful. They hate America, they hate Western civilization, they hate
    white males, they hate rationality. The reasons that leftists give for
    hating the West, etc. clearly do not correspond with their real motiveM
    They SAY they hate the West because it is warlike, imperialistic, sexist,
    ethnocentric and so forth, but where these same faults appear in socialist
    countries or in primitive cultures, the leftist finds excuses for them, or
    at best he GRUDGINGLY admits that they exist; whereas he ENTHUSIASTICALLY
    points out (and often greatly exaggerates) these faults where they appear in
    Western civilization. Thus it is clear that these faults are not the
s real motive for hating AmeM
rica and the West. He hates America and
    the West because they are strong and successful.
 etc., play little role in the liberal and leftist vocabulary.
    The leftist is anti-individualistic, pro-collectivist. He wants society to
s problems for them, satisfy everyone
s needs for them, take
    care of them. He is not the sort of person who has an inner sense of
fidence in his ability to solve his own problems and satisfy his own
    needs. The leftist is antagonistic to the concept of competition because,
    deep inside, he feels like a loser.
17. Art forms that appeal to modern leftish intellectuals tend to focus on
    sordidness, defeat and despair, or else they take an orgiastic tone,
    throwing off rational control as if there were no hope of accomplishing
    anything through rational calculation and all that was left was to immerse
    oneself in the sensM
ations of the moment.
18. Modern leftish philosophers tend to dismiss reason, science, objective
    reality and to insist that everything is culturally relative. It is true
    that one can ask serious questions about the foundations of scientific
    knowledge and about how, if at all, the concept of objective reality can be
    defined. But it is obvious that modern leftish philosophers are not simply
    cool-headed logicians systematically analyzing the foundations of knowledge.
    They are deeply invoM
lved emotionally in their attack on truth and reality.
    They attack these concepts because of their own psychological needs. For one
    thing, their attack is an outlet for hostility, and, to the extent that it
    is successful, it satisfies the drive for power. More importantly, the
    leftist hates science and rationality because they classify certain beliefs
    as true (i.e., successful, superior) and other beliefs as false (i.e.,
    failed, inferior). The leftist
s feelings of inferiority run soM
    cannot tolerate any classification of some things as successful or superior
    and other things as failed or inferior. This also underlies the rejection by
    many leftists of the concept of mental illness and of the utility of IQ
    tests. Leftists are antagonistic to genetic explanations of human abilities
    or behavior because such explanations tend to make some persons appear
    superior or inferior to others. Leftists prefer to give society the credit
    or blame for an individuM
s ability or lack of it. Thus if a person is
 it is not his fault, but society
s, because he has not been
    brought up properly.
19. The leftist is not typically the kind of person whose feelings of
    inferiority make him a braggart, an egotist, a bully, a self-promoter,
    a ruthless competitor. This kind of person has not wholly lost faith in
    himself. He has a deficit in his sense of power and self-worth, but he can
    still conceive of himself as having the capacity to bM
    efforts to make himself strong produce his unpleasant behavior. [1] But the
    leftist is too far gone for that. His feelings of inferiority are so
    ingrained that he cannot conceive of himself as individually strong and
    valuable. Hence the collectivism of the leftist. He can feel strong only as
    a member of a large organization or a mass movement with which he identifies
20. Notice the masochistic tendency of leftist tactics. Leftists protest by
in front of vehicles, they intentionally provoke police or
    racists to abuse them, etc. These tactics may often be effective, but many
    leftists use them not as a means to an end but because they PREFER
    masochistic tactics. Self-hatred is a leftist trait.
21. Leftists may claim that their activism is motivated by compassion or by
    moral principles, and moral principle does play a role for the leftist of
    the oversocialized type. But compassion and moral principle cannot be the
s for leftist activism. Hostility is too prominent a component of
    leftist behavior; so is the drive for power. Moreover, much leftist behavior
    is not rationally calculated to be of benefit to the people whom the
    leftists claim to be trying to help. For example, if one believes that
    affirmative action is good for black people, does it make sense to demand
    affirmative action in hostile or dogmatic terms? Obviously it would be more
    productive to take a diplomatic and conciliatory approachM
 that would make at
    least verbal and symbolic concessions to white people who think that
    affirmative action discriminates against them. But leftist activists do not
    take such an approach because it would not satisfy their emotional needs.
    Helping black people is not their real goal. Instead, race problems serve as
    an excuse for them to express their own hostility and frustrated need for
    power. In doing so they actually harm black people, because the activists
    hostile attitude tM
oward the white majority tends to intensify race hatred.
22. If our society had no social problems at all, the leftists would have to
    INVENT problems in order to provide themselves with an excuse for making
23. We emphasize that the foregoing does not pretend to be an accurate
    description of everyone who might be considered a leftist. It is only
    a rough indication of a general tendency of leftism.
24. Psychologists use the term
 to designate the pM
    children are trained to think and act as society demands. A person is said
    to be well socialized if he believes in and obeys the moral code of his
    society and fits in well as a functioning part of that society. It may seem
    senseless to say that many leftists are oversocialized, since the leftist is
    perceived as a rebel. Nevertheless, the position can be defended. Many
    leftists are not such rebels as they seem.
25. The moral code of our society is so demanding that no oM
    and act in a completely moral way. For example, we are not supposed to hate
    anyone, yet almost everyone hates somebody at some time or other, whether he
    admits it to himself or not. Some people are so highly socialized that the
    attempt to think, feel and act morally imposes a severe burden on them. In
    order to avoid feelings of guilt, they continually have to deceive
    themselves about their own motives and find moral explanations for feelings
    and actions that in M
reality have a non-moral origin. We use the term
 to describe such people. [2]
26. Oversocialization can lead to low self-esteem, a sense of powerlessness,
    defeatism, guilt, etc. One of the most important means by which our society
    socializes children is by making them feel ashamed of behavior or speech
    that is contrary to society
s expectations. If this is overdone, or if
    a particular child is especially susceptible to such feelings, he ends by
    feeling ashamed oM
f HIMSELF. Moreover the thought and the behavior of the
    oversocialized person are more restricted by society
s expectations than are
    those of the lightly socialized person. The majority of people engage in
    a significant amount of naughty behavior. They lie, they commit petty
    thefts, they break traffic laws, they goof off at work, they hate someone,
    they say spiteful things or they use some underhanded trick to get ahead of
    the other guy. The oversocialized person cannot do these thinM
    does do them he generates in himself a sense of shame and self-hatred. The
    oversocialized person cannot even experience, without guilt, thoughts or
    feelings that are contrary to the accepted morality; he cannot think
 thoughts. And socialization is not just a matter of morality; we
    are socialized to conform to many norms of behavior that do not fall under
    the heading of morality. Thus the oversocialized person is kept on
    a psychological leash and spends hM
is life running on rails that society has
    laid down for him. In many oversocialized people this results in a sense of
    constraint and powerlessness that can be a severe hardship. We suggest that
    oversocialization is among the more serious cruelties that human beings
    inflict on one another.
27. We argue that a very important and influential segment of the modern left is
    oversocialized and that their oversocialization is of great importance in
    determining the direction of modern leftism.M
 Leftists of the oversocialized
    type tend to be intellectuals or members of the upper-middle class. Notice
    that university intellectuals [3] constitute the most highly socialized
    segment of our society and also the most left-wing segment.
28. The leftist of the oversocialized type tries to get off his psychological
    leash and assert his autonomy by rebelling. But usually he is not strong
    enough to rebel against the most basic values of society. Generally
    speaking, the goals of today
s leftists are NOT in conflict with the
    accepted morality. On the contrary, the left takes an accepted moral
    principle, adopts it as its own, and then accuses mainstream society of
    violating that principle. Examples: racial equality, equality of the sexes,
    helping poor people, peace as opposed to war, nonviolence generally, freedom
    of expression, kindness to animals. More fundamentally, the duty of the
    individual to serve society and the duty of society to take care of the
ual. All these have been deeply rooted values of our society (or at
    least of its middle and upper classes [4] for a long time. These values are
    explicitly or implicitly expressed or presupposed in most of the material
    presented to us by the mainstream communications media and the educational
    system. Leftists, especially those of the oversocialized type, usually do
    not rebel against these principles but justify their hostility to society by
    claiming (with some degree of truth) that sociM
ety is not living up to these
29. Here is an illustration of the way in which the oversocialized leftist shows
    his real attachment to the conventional attitudes of our society while
    pretending to be in rebellion against it. Many leftists push for affirmative
    action, for moving black people into high-prestige jobs, for improved
    education in black schools and more money for such schools; the way of life
 they regard as a social disgrace. They wanM
    integrate the black man into the system, make him a business executive,
    a lawyer, a scientist just like upper-middle-class white people. The
    leftists will reply that the last thing they want is to make the black man
    into a copy of the white man; instead, they want to preserve African
    American culture. But in what does this preservation of African American
    culture consist? It can hardly consist in anything more than eating
    black-style food, listening to black-style music, wearM
    clothing and going to a black- style church or mosque. In other words, it
    can express itself only in superficial matters. In all ESSENTIAL respects
    most leftists of the oversocialized type want to make the black man conform
    to white, middle-class ideals. They want to make him study technical
    subjects, become an executive or a scientist, spend his life climbing the
    status ladder to prove that black people are as good as white. They want to
    make black fathers
 they want black gangs to become
    nonviolent, etc. But these are exactly the values of the
    industrial-technological system. The system couldn
t care less what kind of
    music a man listens to, what kind of clothes he wears or what religion he
    believes in as long as he studies in school, holds a respectable job, climbs
    the status ladder, is a
 parent, is nonviolent and so forth. In
    effect, however much he may deny it, the oversocialized leftist wants to
ntegrate the black man into the system and make him adopt its values.
30. We certainly do not claim that leftists, even of the oversocialized type,
    NEVER rebel against the fundamental values of our society. Clearly they
    sometimes do. Some oversocialized leftists have gone so far as to rebel
    against one of modern society
s most important principles by engaging in
    physical violence. By their own account, violence is for them a form of
 In other words, by committing violencM
e they break through the
    psychological restraints that have been trained into them. Because they are
    oversocialized these restraints have been more confining for them than for
    others; hence their need to break free of them. But they usually justify
    their rebellion in terms of mainstream values. If they engage in violence
    they claim to be fighting against racism or the like.
31. We realize that many objections could be raised to the foregoing thumbnail
    sketch of leftist psychology. TheM
 real situation is complex, and anything
    like a complete description of it would take several volumes even if the
    necessary data were available. We claim only to have indicated very roughly
    the two most important tendencies in the psychology of modern leftism.
32. The problems of the leftist are indicative of the problems of our society as
    a whole. Low self-esteem, depressive tendencies and defeatism are not
    restricted to the left. Though they are especially noticeable in the left,
ey are widespread in our society. And today
s society tries to socialize
    us to a greater extent than any previous society. We are even told by
    experts how to eat, how to exercise, how to make love, how to raise our kids
33. Human beings have a need (probably based in biology) for something that we
 This is closely related to the need for power
    (which is widely recognized) but is not quite the same thing. The power
ocess has four elements. The three most clear-cut of these we call goal,
    effort and attainment of goal. (Everyone needs to have goals whose
    attainment requires effort, and needs to succeed in attaining at least some
    of his goals.) The fourth element is more difficult to define and may not be
    necessary for everyone. We call it autonomy and will discuss it later
    (paragraphs 42-44).
34. Consider the hypothetical case of a man who can have anything he wants just
    by wishing for it. Such a M
man has power, but he will develop serious
    psychological problems. At first he will have a lot of fun, but by and by he
    will become acutely bored and demoralized. Eventually he may become
    clinically depressed. History shows that leisured aristocracies tend to
    become decadent. This is not true of fighting aristocracies that have to
    struggle to maintain their power. But leisured, secure aristocracies that
    have no need to exert themselves usually become bored, hedonistic and
zed, even though they have power. This shows that power is not
    enough. One must have goals toward which to exercise one
35. Everyone has goals; if nothing else, to obtain the physical necessities of
    life: food, water and whatever clothing and shelter are made necessary by
    the climate. But the leisured aristocrat obtains these things without
    effort. Hence his boredom and demoralization.
36. Nonattainment of important goals results in death if the goals are physical
and in frustration if nonattainment of the goals is compatible
    with survival. Consistent failure to attain goals throughout life results in
    defeatism, low self-esteem or depression.
37, Thus, in order to avoid serious psychological problems, a human being needs
    goals whose attainment requires effort, and he must have a reasonable rate
    of success in attaining his goals.
SURROGATE ACTIVITIES
38. But not every leisured aristocrat becomes bored and demoralized. For
    example, the emperor HirohM
ito, instead of sinking into decadent hedonism,
    devoted himself to marine biology, a field in which he became distinguished.
    When people do not have to exert themselves to satisfy their physical needs
    they often set up artificial goals for themselves. In many cases they then
    pursue these goals with the same energy and emotional involvement that they
    otherwise would have put into the search for physical necessities. Thus the
    aristocrats of the Roman Empire had their literary pretensionsM
    European aristocrats a few centuries ago invested tremendous time and energy
    in hunting, though they certainly didn
t need the meat; other aristocracies
    have competed for status through elaborate displays of wealth; and a few
    aristocrats, like Hirohito, have turned to science.
39. We use the term
 to designate an activity that is
    directed toward an artificial goal that people set up for themselves merely
    in order to have some goal to work toward, or leM
t us say, merely for the
 that they get from pursuing the goal. Here is
    a rule of thumb for the identification of surrogate activities. Given
    a person who devotes much time and energy to the pursuit of goal X, ask
    yourself this: If he had to devote most of his time and energy to satisfying
    his biological needs, and if that effort required him to use his physical
    and mental faculties in a varied and interesting way, would he feel
    seriously deprived becaM
use he did not attain goal X? If the answer is no,
s pursuit of goal X is a surrogate activity. Hirohito
    studies in marine biology clearly constituted a surrogate activity, since it
    is pretty certain that if Hirohito had had to spend his time working at
    interesting non-scientific tasks in order to obtain the necessities of life,
    he would not have felt deprived because he didn
t know all about the anatomy
    and life-cycles of marine animals. On the other hand the pM
    love (for example) is not a surrogate activity, because most people, even if
    their existence were otherwise satisfactory, would feel deprived if they
    passed their lives without ever having a relationship with a member of the
    opposite sex. (But pursuit of an excessive amount of sex, more than one
    really needs, can be a surrogate activity.)
40. In modern industrial society only minimal effort is necessary to satisfy
s physical needs. It is enough to go through a M
training program to
    acquire some petty technical skill, then come to work on time and exert the
    very modest effort needed to hold a job. The only requirements are
    a moderate amount of intelligence and, most of all, simple OBEDIENCE. If one
    has those, society takes care of one from cradle to grave. (Yes, there is an
    underclass that cannot take the physical necessities for granted, but we are
    speaking here of mainstream society.) Thus it is not surprising that modern
    society is fullM
 of surrogate activities. These include scientific work,
    athletic achievement, humanitarian work, artistic and literary creation,
    climbing the corporate ladder, acquisition of money and material goods far
    beyond the point at which they cease to give any additional physical
    satisfaction, and social activism when it addresses issues that are not
    important for the activist personally, as in the case of white activists who
    work for the rights of nonwhite minorities. These are not always PUM
    surrogate activities, since for many people they may be motivated in part by
    needs other than the need to have some goal to pursue. Scientific work may
    be motivated in part by a drive for prestige, artistic creation by a need to
    express feelings, militant social activism by hostility. But for most people
    who pursue them, these activities are in large part surrogate activities.
    For example, the majority of scientists will probably agree that the
heir work is more important than the money and
    prestige they earn.
41. For many if not most people, surrogate activities are less satisfying than
    the pursuit of real goals (that is, goals that people would want to attain
    even if their need for the power process were already fulfilled). One
    indication of this is the fact that, in many or most cases, people who are
    deeply involved in surrogate activities are never satisfied, never at rest.
    Thus the money-maker constantly strives for morM
e and more wealth. The
    scientist no sooner solves one problem than he moves on to the next. The
    long-distance runner drives himself to run always farther and faster. Many
    people who pursue surrogate activities will say that they get far more
    fulfillment from these activities than they do from the
    of satisfying their biological needs, but that is because in our society the
    effort needed to satisfy the biological needs has been reduced to
    triviality. More impoM
rtantly, in our society people do not satisfy their
    biological needs AUTONOMOUSLY but by functioning as parts of an immense
    social machine. In contrast, people generally have a great deal of autonomy
    in pursuing their surrogate activities.
42. Autonomy as a part of the power process may not be necessary for every
    individual. But most people need a greater or lesser degree of autonomy in
    working toward their goals. Their efforts must be undertaken on their own
    initiative and M
must be under their own direction and control. Yet most
    people do not have to exert this initiative, direction and control as single
    individuals. It is usually enough to act as a member of a SMALL group. Thus
    if half a dozen people discuss a goal among themselves and make a successful
    joint effort to attain that goal, their need for the power process will be
    served. But if they work under rigid orders handed down from above that
    leave them no room for autonomous decision and initiativeM
    for the power process will not be served. The same is true when decisions
    are made on a collective basis if the group making the collective decision
    is so large that the role of each individual is insignificant. [5]
43. It is true that some individuals seem to have little need for autonomy.
    Either their drive for power is weak or they satisfy it by identifying
    themselves with some powerful organization to which they belong. And then
    there are unthinking, animal typesM
 who seem to be satisfied with a purely
    physical sense of power (the good combat soldier, who gets his sense of
    power by developing fighting skills that he is quite content to use in blind
    obedience to his superiors).
44. But for most people it is through the power process
having a goal, making an
    AUTONOMOUS effort and attaining the goal
that self-esteem, self-confidence
    and a sense of power are acquired. When one does not have adequate
    opportunity to go through the power process M
the consequences are (depending
    on the individual and on the way the power process is disrupted) boredom,
    demoralization, low self-esteem, inferiority feelings, defeatism,
    depression, anxiety, guilt, frustration, hostility, spouse or child abuse,
    insatiable hedonism, abnormal sexual behavior, sleep disorders, eating
    disorders, etc. [6]
SOURCES OF SOCIAL PROBLEMS
45. Any of the foregoing symptoms can occur in any society, but in modern
    industrial society they are present on a massive M
    to mention that the world today seems to be going crazy. This sort of thing
    is not normal for human societies. There is good reason to believe that
    primitive man suffered from less stress and frustration and was better
    satisfied with his way of life than modern man is. It is true that not all
    was sweetness and light in primitive societies. Abuse of women was common
    among the Australian aborigines, transexuality was fairly common among some
can Indian tribes. But it does appear that GENERALLY SPEAKING
    the kinds of problems that we have listed in the preceding paragraph were
    far less common among primitive peoples than they are in modern society.
46. We attribute the social and psychological problems of modern society to the
    fact that that society requires people to live under conditions radically
    different from those under which the human race evolved and to behave in
    ways that conflict with the patterns of behavior that the M
    developed while living under the earlier conditions. It is clear from what
    we have already written that we consider lack of opportunity to properly
    experience the power process as the most important of the abnormal
    conditions to which modern society subjects people. But it is not the only
    one. Before dealing with disruption of the power process as a source of
    social problems we will discuss some of the other sources.
47. Among the abnormal conditions present in modern indusM
    excessive density of population, isolation of man from nature, excessive
    rapidity of social change and the breakdown of natural small-scale
    communities such as the extended family, the village or the tribe.
48. It is well known that crowding increases stress and aggression. The degree
    of crowding that exists today and the isolation of man from nature are
    consequences of technological progress. All pre-industrial societies were
    predominantly rural. The Industrial RevoM
lution vastly increased the size of
    cities and the proportion of the population that lives in them, and modern
    agricultural technology has made it possible for the Earth to support a far
    denser population than it ever did before. (Also, technology exacerbates the
    effects of crowding because it puts increased disruptive powers in people
    hands. For example, a variety of noise- making devices: power mowers,
    radios, motorcycles, etc. If the use of these devices is unrestricted,
ople who want peace and quiet are frustrated by the noise. If their use is
    restricted, people who use the devices are frustrated by the regulations.
    But if these machines had never been invented there would have been no
    conflict and no frustration generated by them.)
49. For primitive societies the natural world (which usually changes only
    slowly) provided a stable framework and therefore a sense of security. In
    the modern world it is human society that dominates nature rather than the
  other way around, and modern society changes very rapidly owing to
    technological change. Thus there is no stable framework.
50. The conservatives are fools: They whine about the decay of traditional
    values, yet they enthusiastically support technological progress and
    economic growth. Apparently it never occurs to them that you can
    rapid, drastic changes in the technology and the economy of a society
    without causing rapid changes in all other aspects of the society as well,
and that such rapid changes inevitably break down traditional values.
51. The breakdown of traditional values to some extent implies the breakdown of
    the bonds that hold together traditional small-scale social groups. The
    disintegration of small-scale social groups is also promoted by the fact
    that modern conditions often require or tempt individuals to move to new
    locations, separating themselves from their communities. Beyond that,
    a technological society HAS TO weaken family ties and loM
    it is to function efficiently. In modern society an individual
    must be first to the system and only secondarily to a small-scale community,
    because if the internal loyalties of small-scale communities were stronger
    than loyalty to the system, such communities would pursue their own
    advantage at the expense of the system.
52. Suppose that a public official or a corporation executive appoints his
    cousin, his friend or his co- religionist to a position rathM
    appointing the person best qualified for the job. He has permitted personal
    loyalty to supersede his loyalty to the system, and that is
 both of which are terrible sins in modern society.
    Would-be industrial societies that have done a poor job of subordinating
    personal or local loyalties to loyalty to the system are usually very
    inefficient. (Look at Latin America.) Thus an advanced industrial society
    can tolerate only those small-scaM
le communities that are emasculated, tamed
    and made into tools of the system. [7]
53. Crowding, rapid change and the breakdown of communities have been widely
    recognized as sources of social problems. But we do not believe they are
    enough to account for the extent of the problems that are seen today.
54. A few pre-industrial cities were very large and crowded, yet their
    inhabitants do not seem to have suffered from psychological problems to the
    same extent as modern man. In America today M
there still are uncrowded rural
    areas, and we find there the same problems as in urban areas, though the
    problems tend to be less acute in the rural areas. Thus crowding does not
    seem to be the decisive factor.
55. On the growing edge of the American frontier during the 19th century, the
    mobility of the population probably broke down extended families and
    small-scale social groups to at least the same extent as these are broken
    down today. In fact, many nuclear families lived by choicM
    isolation, having no neighbors within several miles, that they belonged to
    no community at all, yet they do not seem to have developed problems as
56. Furthermore, change in American frontier society was very rapid and deep.
    A man might be born and raised in a log cabin, outside the reach of law and
    order and fed largely on wild meat; and by the time he arrived at old age he
    might be working at a regular job and living in an ordered community with
w enforcement. This was a deeper change than that which
    typically occurs in the life of a modern individual, yet it does not seem to
    have led to psychological problems. In fact, 19th century American society
    had an optimistic and self-confident tone, quite unlike that of today
57. The difference, we argue, is that modern man has the sense (largely
    justified) that change is IMPOSED on him, whereas the 19th century
    frontiersman had the sense (also largely justified) thaM
t he created change
    himself, by his own choice. Thus a pioneer settled on a piece of land of his
    own choosing and made it into a farm through his own effort. In those days
    an entire county might have only a couple of hundred inhabitants and was
    a far more isolated and autonomous entity than a modern county is. Hence the
    pioneer farmer participated as a member of a relatively small group in the
    creation of a new, ordered community. One may well question whether the
    creation of thisM
 community was an improvement, but at any rate it satisfied
s need for the power process.
58. It would be possible to give other examples of societies in which there has
    been rapid change and/or lack of close community ties without the kind of
    massive behavioral aberration that is seen in today
s industrial society. We
    contend that the most important cause of social and psychological problems
    in modern society is the fact that people have insufficient opportunity to
 through the power process in a normal way. We don
    modern society is the only one in which the power process has been
    disrupted. Probably most if not all civilized societies have interfered with
    the power process to a greater or lesser extent. But in modern industrial
    society the problem has become particularly acute. Leftism, at least in its
    recent (mid- to late-20th century) form, is in part a symptom of deprivation
    with respect to the power process.
OF THE POWER PROCESS IN MODERN SOCIETY
59. We divide human drives into three groups: (1) those drives that can be
    satisfied with minimal effort; (2) those that can be satisfied but only at
    the cost of serious effort; (3) those that cannot be adequately satisfied no
    matter how much effort one makes. The power process is the process of
    satisfying the drives of the second group. The more drives there are in the
    third group, the more there is frustration, anger, eventually defeatism,
60. In modern industrial society natural human drives tend to be pushed into the
    first and third groups, and the second group tends to consist increasingly
    of artificially created drives.
61. In primitive societies, physical necessities generally fall into group 2:
    They can be obtained, but only at the cost of serious effort. But modern
    society tends to guaranty the physical necessities to everyone [9] in
    exchange for only minimal effort, hence physical needs are pushed into M
    1. (There may be disagreement about whether the effort needed to hold a job
; but usually, in lower- to middle- level jobs, whatever effort
    is required is merely that of OBEDIENCE. You sit or stand where you are told
    to sit or stand and do what you are told to do in the way you are told to do
    it. Seldom do you have to exert yourself seriously, and in any case you have
    hardly any autonomy in work, so that the need for the power process is not
. Social needs, such as sex, love and status, often remain in group 2 in
    modern society, depending on the situation of the individual. [10] But,
    except for people who have a particularly strong drive for status, the
    effort required to fulfill the social drives is insufficient to satisfy
    adequately the need for the power process.
63. So certain artificial needs have been created that fall into group 2, hence
    serve the need for the power process. Advertising and marketing techniques
e been developed that make many people feel they need things that their
    grandparents never desired or even dreamed of. It requires serious effort to
    earn enough money to satisfy these artificial needs, hence they fall into
    group 2. (But see paragraphs 80-82.) Modern man must satisfy his need for
    the power process largely through pursuit of the artificial needs created by
    the advertising and marketing industry [11], and through surrogate
64. It seems that for many people, mM
aybe the majority, these artificial forms of
    the power process are insufficient. A theme that appears repeatedly in the
    writings of the social critics of the second half of the 20th century is the
    sense of purposelessness that afflicts many people in modern society. (This
    purposelessness is often called by other names such as
middle-class vacuity.
) We suggest that the so-called
    actually a search for a sense of purpose, often for commitmentM
    surrogate activity. It may be that existentialism is in large part
    a response to the purposelessness of modern life. [12] Very widespread in
    modern society is the search for
 But we think that for the
    majority of people an activity whose main goal is fulfillment (that is,
    a surrogate activity) does not bring completely satisfactory fulfillment. In
    other words, it does not fully satisfy the need for the power process. (See
    paragraph 41.) That need cM
an be fully satisfied only through activities that
    have some external goal, such as physical necessities, sex, love, status,
65. Moreover, where goals are pursued through earning money, climbing the status
    ladder or functioning as part of the system in some other way, most people
    are not in a position to pursue their goals AUTONOMOUSLY. Most workers are
s employee and, as we pointed out in paragraph 61, must spend
    their days doing what they are told to do M
in the way they are told to do it.
    Even people who are in business for themselves have only limited autonomy.
    It is a chronic complaint of small-business persons and entrepreneurs that
    their hands are tied by excessive government regulation. Some of these
    regulations are doubtless unnecessary, but for the most part government
    regulations are essential and inevitable parts of our extremely complex
    society. A large portion of small business today operates on the franchise
 was reported in the Wall Street Journal a few years ago that many
    of the franchise-granting companies require applicants for franchises to
    take a personality test that is designed to EXCLUDE those who have
    creativity and initiative, because such persons are not sufficiently docile
    to go along obediently with the franchise system. This excludes from small
    business many of the people who most need autonomy.
66. Today people live more by virtue of what the system does FOR them or TO them
  than by virtue of what they do for themselves. And what they do for
    themselves is done more and more along channels laid down by the system.
    Opportunities tend to be those that the system provides, the opportunities
    must be exploited in accord with rules and regulations [13], and techniques
    prescribed by experts must be followed if there is to be a chance of
67. Thus the power process is disrupted in our society through a deficiency of
    real goals and a deficiency of autonomM
y in the pursuit of goals. But it is
    also disrupted because of those human drives that fall into group 3: the
    drives that one cannot adequately satisfy no matter how much effort one
    makes. One of these drives is the need for security. Our lives depend on
    decisions made by other people; we have no control over these decisions and
    usually we do not even know the people who make them. (
    in which relatively few people
Philip B. Heymann of Harvard Law School, quoted by Anthony Lewis,
    New York Times, April 21, 1995.) Our lives depend on whether safety
    standards at a nuclear power plant are properly maintained; on how much
    pesticide is allowed to get into our food or how much pollution into our
    air; on how skillful (or incompetent) our doctor is; whether we lose or get
    a job may depend on decisions made by government economists or corporation
    executives; and so forth. Most individuals are noM
t in a position to secure
    themselves against these threats to more [than] a very limited extent. The
s search for security is therefore frustrated, which leads to
    a sense of powerlessness.
68. It may be objected that primitive man is physically less secure than modern
    man, as is shown by his shorter life expectancy; hence modern man suffers
    from less, not more than the amount of insecurity that is normal for human
    beings. But psychological security does not closely corresM
    security. What makes us FEEL secure is not so much objective security as
    a sense of confidence in our ability to take care of ourselves. Primitive
    man, threatened by a fierce animal or by hunger, can fight in self-defense
    or travel in search of food. He has no certainty of success in these
    efforts, but he is by no means helpless against the things that threaten
    him. The modern individual on the other hand is threatened by many things
    against which he is helplessM
: nuclear accidents, carcinogens in food,
    environmental pollution, war, increasing taxes, invasion of his privacy by
    large organizations, nationwide social or economic phenomena that may
    disrupt his way of life.
69. It is true that primitive man is powerless against some of the things that
    threaten him; disease for example. But he can accept the risk of disease
    stoically. It is part of the nature of things, it is no one
    it is the fault of some imaginary, impersonal dM
emon. But threats to the
    modern individual tend to be MAN-MADE. They are not the results of chance
    but are IMPOSED on him by other persons whose decisions he, as an
    individual, is unable to influence. Consequently he feels frustrated,
    humiliated and angry.
70. Thus primitive man for the most part has his security in his own hands
    (either as an individual or as a member of a SMALL group) whereas the
    security of modern man is in the hands of persons or organizations that are
mote or too large for him to be able personally to influence them. So
s drive for security tends to fall into groups 1 and 3; in some
    areas (food, shelter etc.) his security is assured at the cost of only
    trivial effort, whereas in other areas he CANNOT attain security. (The
    foregoing greatly simplifies the real situation, but it does indicate in
    a rough, general way how the condition of modern man differs from that of
71. People have many transitory driveM
s or impulses that are necessarily
    frustrated in modern life, hence fall into group 3. One may become angry,
    but modern society cannot permit fighting. In many situations it does not
    even permit verbal aggression. When going somewhere one may be in a hurry,
    or one may be in a mood to travel slowly, but one generally has no choice
    but to move with the flow of traffic and obey the traffic signals. One may
s work in a different way, but usually one can work only
rding to the rules laid down by one
s employer. In many other ways as
    well, modern man is strapped down by a network of rules and regulations
    (explicit or implicit) that frustrate many of his impulses and thus
    interfere with the power process. Most of these regulations cannot be
    dispensed with, because they are necessary for the functioning of industrial
72. Modern society is in certain respects extremely permissive. In matters that
    are irrelevant to the functioning of the M
system we can generally do what we
    please. We can believe in any religion we like (as long as it does not
    encourage behavior that is dangerous to the system). We can go to bed with
    anyone we like (as long as we practice
). We can do anything we
    like as long as it is UNIMPORTANT. But in all IMPORTANT matters the system
    tends increasingly to regulate our behavior.
73. Behavior is regulated not only through explicit rules and not only by the
    government. Control is often exeM
rcised through indirect coercion or through
    psychological pressure or manipulation, and by organizations other than the
    government, or by the system as a whole. Most large organizations use some
    form of propaganda [14] to manipulate public attitudes or behavior.
    Propaganda is not limited to
 and advertisements, and sometimes
    it is not even consciously intended as propaganda by the people who make it.
    For instance, the content of entertainment programming is a powerful M
    propaganda. An example of indirect coercion: There is no law that says we
    have to go to work every day and follow our employer
s orders. Legally there
    is nothing to prevent us from going to live in the wild like primitive
    people or from going into business for ourselves. But in practice there is
    very little wild country left, and there is room in the economy for only
    a limited number of small business owners. Hence most of us can survive only
74. We suggest that modern man
s obsession with longevity, and with maintaining
    physical vigor and sexual attractiveness to an advanced age, is a symptom of
    unfulfillment resulting from deprivation with respect to the power process.
 also is such a symptom. So is the lack of interest in
    having children that is fairly common in modern society but almost
    unheard-of in primitive societies.
75. In primitive societies life is a succession of stages. The needs and
    purposes of one stage having been fulfilled, there is no particular
    reluctance about passing on to the next stage. A young man goes through the
    power process by becoming a hunter, hunting not for sport or for fulfillment
    but to get meat that is necessary for food. (In young women the process is
    more complex, with greater emphasis on social power; we won
    here.) This phase having been successfully passed through, the young man has
    no reluctance about settling down toM
 the responsibilities of raising
    a family. (In contrast, some modern people indefinitely postpone having
    children because they are too busy seeking some kind of
    suggest that the fulfillment they need is adequate experience of the power
with real goals instead of the artificial goals of surrogate
    activities.) Again, having successfully raised his children, going through
    the power process by providing them with the physical necessities, the
man feels that his work is done and he is prepared to accept old
    age (if he survives that long) and death. Many modern people, on the other
    hand, are disturbed by the prospect of physical deterioration and death, as
    is shown by the amount of effort they expend trying to maintain their
    physical condition, appearance and health. We argue that this is due to
    unfulfillment resulting from the fact that they have never put their
    physical powers to any practical use, have never gone through tM
    process using their bodies in a serious way. It is not the primitive man,
    who has used his body daily for practical purposes, who fears the
    deterioration of age, but the modern man, who has never had a practical use
    for his body beyond walking from his car to his house. It is the man whose
    need for the power process has been satisfied during his life who is best
    prepared to accept the end of that life.
76. In response to the arguments of this section someone will say,
    find a way to give people the opportunity to go through the power process.
    For such people the value of the opportunity is destroyed by the very fact
    that society gives it to them. What they need is to find or make their own
    opportunities. As long as the system GIVES them their opportunities it still
    has them on a leash. To attain autonomy they must get off that leash.
HOW SOME PEOPLE ADJUST
77. Not everyone in industrial-technological society suffers from psychological
roblems. Some people even profess to be quite satisfied with society as it
    is. We now discuss some of the reasons why people differ so greatly in their
    response to modern society.
78. First, there doubtless are differences in the strength of the drive for
    power. Individuals with a weak drive for power may have relatively little
    need to go through the power process, or at least relatively little need for
    autonomy in the power process. These are docile types who would have been
 plantation darkies in the Old South. (We don
t mean to sneer at the
 of the Old South. To their credit, most of the slaves
    were NOT content with their servitude. We do sneer at people who ARE content
    with servitude.)
79. Some people may have some exceptional drive, in pursuing which they satisfy
    their need for the power process. For example, those who have an unusually
    strong drive for social status may spend their whole lives climbing the
    status ladder withM
out ever getting bored with that game.
80. People vary in their susceptibility to advertising and marketing techniques.
    Some are so susceptible that, even if they make a great deal of money, they
    cannot satisfy their constant craving for the the shiny new toys that the
    marketing industry dangles before their eyes. So they always feel
    hard-pressed financially even if their income is large, and their cravings
81. Some people have low susceptibility to advertising and marketiM
    These are the people who aren
t interested in money. Material acquisition
    does not serve their need for the power process.
82. People who have medium susceptibility to advertising and marketing
    techniques are able to earn enough money to satisfy their craving for goods
    and services, but only at the cost of serious effort (putting in overtime,
    taking a second job, earning promotions, etc.). Thus material acquisition
    serves their need for the power process. But it does M
not necessarily follow
    that their need is fully satisfied. They may have insufficient autonomy in
    the power process (their work may consist of following orders) and some of
    their drives may be frustrated (e.g., security, aggression). (We are guilty
    of oversimplification in paragraphs 80- 82 because we have assumed that the
    desire for material acquisition is entirely a creation of the advertising
    and marketing industry. Of course it
s not that simple. [11]
83. Some people partly satiM
sfy their need for power by identifying themselves
    with a powerful organization or mass movement. An individual lacking goals
    or power joins a movement or an organization, adopts its goals as his own,
    then works toward those goals. When some of the goals are attained, the
    individual, even though his personal efforts have played only an
    insignificant part in the attainment of the goals, feels (through his
    identification with the movement or organization) as if he had gone through
he power process. This phenomenon was exploited by the fascists, nazis and
    communists. Our society uses it too, though less crudely. Example: Manuel
    Noriega was an irritant to the U.S. (goal: punish Noriega). The U.S. invaded
    Panama (effort) and punished Noriega (attainment of goal). Thus the U.S.
    went through the power process and many Americans, because of their
    identification with the U.S., experienced the power process vicariously.
    Hence the widespread public approval of the PanamaM
 invasion; it gave people
    a sense of power. [15] We see the same phenomenon in armies, corporations,
    political parties, humanitarian organizations, religious or ideological
    movements. In particular, leftist movements tend to attract people who are
    seeking to satisfy their need for power. But for most people identification
    with a large organization or a mass movement does not fully satisfy the need
84. Another way in which people satisfy their need for the power process is
    through surrogate activities. As we explained in paragraphs 38-40,
    a surrogate activity is an activity that is directed toward an artificial
    goal that the individual pursues for the sake of the
    gets from pursuing the goal, not because he needs to attain the goal itself.
    For instance, there is no practical motive for building enormous muscles,
    hitting a little ball into a hole or acquiring a complete series of postage
    stamps. Yet many people in our society M
devote themselves with passion to
    bodybuilding, golf or stamp-collecting. Some people are more
 than others, and therefore will more readily attach
    importance to a surrogate activity simply because the people around them
    treat it as important or because society tells them it is important. That is
    why some people get very serious about essentially trivial activities such
    as sports, or bridge, or chess, or arcane scholarly pursuits, whereas others
    who are more clM
ear-sighted never see these things as anything but the
    surrogate activities that they are, and consequently never attach enough
    importance to them to satisfy their need for the power process in that way.
    It only remains to point out that in many cases a person
    a living is also a surrogate activity. Not a PURE surrogate activity, since
    part of the motive for the activity is to gain the physical necessities and
    (for some people) social status and the luxuries that adveM
    want. But many people put into their work far more effort than is necessary
    to earn whatever money and status they require, and this extra effort
    constitutes a surrogate activity. This extra effort, together with the
    emotional investment that accompanies it, is one of the most potent forces
    acting toward the continual development and perfecting of the system, with
    negative consequences for individual freedom (see paragraph 131).
    Especially, for the most creativeM
 scientists and engineers, work tends to be
    largely a surrogate activity. This point is so important that it deserves
    a separate discussion, which we shall give in a moment (paragraphs 87-92).
85. In this section we have explained how many people in modern society do
    satisfy their need for the power process to a greater or lesser extent. But
    we think that for the majority of people the need for the power process is
    not fully satisfied. In the first place, those who have an insatiable driveM
    for status, or who get firmly
 on a surrogate activity, or who
    identify strongly enough with a movement or organization to satisfy their
    need for power in that way, are exceptional personalities. Others are not
    fully satisfied with surrogate activities or by identification with an
    organization (see paragraphs 41, 64). In the second place, too much control
    is imposed by the system through explicit regulation or through
    socialization, which results in a deficiency of auM
tonomy, and in frustration
    due to the impossibility of attaining certain goals and the necessity of
    restraining too many impulses.
86. But even if most people in industrial-technological society were well
    satisfied, we (FC) would still be opposed to that form of society, because
    (among other reasons) we consider it demeaning to fulfill one
    power process through surrogate activities or through identification with an
    organization, rather than through pursuit of real goaM
THE MOTIVES OF SCIENTISTS
87. Science and technology provide the most important examples of surrogate
    activities. Some scientists claim that they are motivated by
 But it is easy to see that neither of
    these can be the principal motive of most scientists. As for
    that notion is simply absurd. Most scientists work on highly specialized
    problems that are not the object of any normal curiosity. For example, is an
   astronomer, a mathematician or an entomologist curious about the properties
    of isopropyltrimethylmethane? Of course not. Only a chemist is curious about
    such a thing, and he is curious about it only because chemistry is his
    surrogate activity. Is the chemist curious about the appropriate
    classification of a new species of beetle? No. That question is of interest
    only to the entomologist, and he is interested in it only because entomology
    is his surrogate activity. If the chemist andM
 the entomologist had to exert
    themselves seriously to obtain the physical necessities, and if that effort
    exercised their abilities in an interesting way but in some nonscientific
    pursuit, then they wouldn
t give a damn about isopropyltrimethylmethane or
    the classification of beetles. Suppose that lack of funds for postgraduate
    education had led the chemist to become an insurance broker instead of
    a chemist. In that case he would have been very interested in insurance
ut would have cared nothing about isopropyltrimethylmethane. In any
    case it is not normal to put into the satisfaction of mere curiosity the
    amount of time and effort that scientists put into their work. The
 explanation for the scientists
t work any better. Some
    scientific work has no conceivable relation to the welfare of the human
most of archaeology or comparative linguistics M
for example. Some other
    areas of science present obviously dangerous possibilities. Yet scientists
    in these areas are just as enthusiastic about their work as those who
    develop vaccines or study air pollution. Consider the case of Dr. Edward
    Teller, who had an obvious emotional involvement in promoting nuclear power
    plants. Did this involvement stem from a desire to benefit humanity? If so,
t Dr. Teller get emotional about other
 such a humanitarian then why did he help to develop the H- bomb?
    As with many other scientific achievements, it is very much open to question
    whether nuclear power plants actually do benefit humanity. Does the cheap
    electricity outweigh the accumulating waste and the risk of accidents? Dr.
    Teller saw only one side of the question. Clearly his emotional involvement
    with nuclear power arose not from a desire to
    a personal fulfillment he got from his work M
and from seeing it put to
89. The same is true of scientists generally. With possible rare exceptions,
    their motive is neither curiosity nor a desire to benefit humanity but the
    need to go through the power process: to have a goal (a scientific problem
    to solve), to make an effort (research) and to attain the goal (solution of
    the problem.) Science is a surrogate activity because scientists work mainly
    for the fulfillment they get out of the work itself.
s not that simple. Other motives do play a role for many
    scientists. Money and status for example. Some scientists may be persons of
    the type who have an insatiable drive for status (see paragraph 79) and this
    may provide much of the motivation for their work. No doubt the majority of
    scientists, like the majority of the general population, are more or less
    susceptible to advertising and marketing techniques and need money to
    satisfy their craving for goods and services. Thus sciM
    surrogate activity. But it is in large part a surrogate activity.
91. Also, science and technology constitute a power mass movement, and many
    scientists gratify their need for power through identification with this
    mass movement (see paragraph 83).
92. Thus science marches on blindly, without regard to the real welfare of the
    human race or to any other standard, obedient only to the psychological
    needs of the scientists and of the government officials and corporation
   executives who provide the funds for research.
THE NATURE OF FREEDOM
93. We are going to argue that industrial-technological society cannot be
    reformed in such a way as to prevent it from progressively narrowing the
    sphere of human freedom. But, because
 is a word that can be
    interpreted in many ways, we must first make clear what kind of freedom we
    are concerned with.
 we mean the opportunity to go through the power process, with
    real goals not the arM
tificial goals of surrogate activities, and without
    interference, manipulation or supervision from anyone, especially from any
    large organization. Freedom means being in control (either as an individual
    or as a member of a SMALL group) of the life-and-death issues of one
    existence; food, clothing, shelter and defense against whatever threats
    there may be in one
s environment. Freedom means having power; not the power
    to control other people but the power to control the circumstanM
    own life. One does not have freedom if anyone else (especially a large
    organization) has power over one, no matter how benevolently, tolerantly and
    permissively that power may be exercised. It is important not to confuse
    freedom with mere permissiveness (see paragraph 72).
95. It is said that we live in a free society because we have a certain number
    of constitutionally guaranteed rights. But these are not as important as
    they seem. The degree of personal freedom that eM
xists in a society is
    determined more by the economic and technological structure of the society
    than by its laws or its form of government. [16] Most of the Indian nations
    of New England were monarchies, and many of the cities of the Italian
    Renaissance were controlled by dictators. But in reading about these
    societies one gets the impression that they allowed far more personal
    freedom than our society does. In part this was because they lacked
    efficient mechanisms for enforcing M
s will: There were no modern,
    well-organized police forces, no rapid long-distance communications, no
    surveillance cameras, no dossiers of information about the lives of average
    citizens. Hence it was relatively easy to evade control.
96. As for our constitutional rights, consider for example that of freedom of
    the press. We certainly don
t mean to knock that right; it is very important
    tool for limiting concentration of political power and for keeping those who
political power in line by publicly exposing any misbehavior on
    their part. But freedom of the press is of very little use to the average
    citizen as an individual. The mass media are mostly under the control of
    large organizations that are integrated into the system. Anyone who has
    a little money can have something printed, or can distribute it on the
    Internet or in some such way, but what he has to say will be swamped by the
    vast volume of material put out by the media, hence it will M
    practical effect. To make an impression on society with words is therefore
    almost impossible for most individuals and small groups. Take us (FC) for
    example. If we had never done anything violent and had submitted the present
    writings to a publisher, they probably would not have been accepted. If they
    had been been accepted and published, they probably would not have attracted
    many readers, because it
s more fun to watch the entertainment put out by
    the media than to reaM
d a sober essay. Even if these writings had had many
    readers, most of these readers would soon have forgotten what they had read
    as their minds were flooded by the mass of material to which the media
    expose them. In order to get our message before the public with some chance
    of making a lasting impression, we
ve had to kill people.
97. Constitutional rights are useful up to a point, but they do not serve to
    guarantee much more than what might be called the bourgeois conception of
eedom. According to the bourgeois conception, a
 man is essentially
    an element of a social machine and has only a certain set of prescribed and
    delimited freedoms; freedoms that are designed to serve the needs of the
    social machine more than those of the individual. Thus the bourgeois
 man has economic freedom because that promotes growth and progress;
    he has freedom of the press because public criticism restrains misbehavior
    by political leaders; he has a rightM
 to a fair trial because imprisonment at
    the whim of the powerful would be bad for the system. This was clearly the
    attitude of Simon Bolivar. To him, people deserved liberty only if they used
    it to promote progress (progress as conceived by the bourgeois). Other
    bourgeois thinkers have taken a similar view of freedom as a mere means to
    collective ends. Chester C. Tan,
Chinese Political Thought in the Twentieth
 page 202, explains the philosophy of the Kuomintang leader HM
An individual is granted rights because he is a member of society
    and his community life requires such rights. By community Hu meant the whole
    society of the nation.
 And on page 259 Tan states that according to Carsum
    Chang (Chang Chun-mai, head of the State Socialist Party in China) freedom
    had to be used in the interest of the state and of the people as a whole.
    But what kind of freedom does one have if one can use it only as someone
    else prescribes? FC
eption of freedom is not that of Bolivar, Hu,
    Chang or other bourgeois theorists. The trouble with such theorists is that
    they have made the development and application of social theories their
    surrogate activity. Consequently the theories are designed to serve the
    needs of the theorists more than the needs of any people who may be unlucky
    enough to live in a society on which the theories are imposed.
98. One more point to be made in this section: It should not be assumed that
n has enough freedom just because he SAYS he has enough. Freedom is
    restricted in part by psychological controls of which people are
    unconscious, and moreover many people
s ideas of what constitutes freedom
    are governed more by social convention than by their real needs. For
s likely that many leftists of the oversocialized type would say
    that most people, including themselves, are socialized too little rather
    than too much, yet the oversocialized leftist pays a heavy pM
    price for his high level of socialization.
SOME PRINCIPLES OF HISTORY
99. Think of history as being the sum of two components: an erratic component
    that consists of unpredictable events that follow no discernible pattern,
    and a regular component that consists of long-term historical trends. Here
    we are concerned with the long-term trends.
100. FIRST PRINCIPLE. If a SMALL change is made that affects a long-term
     historical trend, then the effect of that change will almost alwM
the trend will soon revert to its original state. (Example:
     A reform movement designed to clean up political corruption in a society
     rarely has more than a short-term effect; sooner or later the reformers
     relax and corruption creeps back in. The level of political corruption in
     a given society tends to remain constant, or to change only slowly with the
     evolution of the society. Normally, a political cleanup will be permanent
     only if accompanied by widespM
read social changes; a SMALL change in the
t be enough.) If a small change in a long-term historical trend
     appears to be permanent, it is only because the change acts in the
     direction in which the trend is already moving, so that the trend is not
     altered by only pushed a step ahead.
101. The first principle is almost a tautology. If a trend were not stable with
     respect to small changes, it would wander at random rather than following
     a definite direction; in other M
words it would not be a long- term trend at
102. SECOND PRINCIPLE. If a change is made that is sufficiently large to alter
     permanently a long-term historical trend, then it will alter the society as
     a whole. In other words, a society is a system in which all parts are
     interrelated, and you can
t permanently change any important part without
     changing all other parts as well.
103. THIRD PRINCIPLE. If a change is made that is large enough to alter
     permanently a long-term trM
end, then the consequences for the society as
     a whole cannot be predicted in advance. (Unless various other societies
     have passed through the same change and have all experienced the same
     consequences, in which case one can predict on empirical grounds that
     another society that passes through the same change will be like to
     experience similar consequences.)
104. FOURTH PRINCIPLE. A new kind of society cannot be designed on paper. That
     is, you cannot plan out a new form of societM
y in advance, then set it up
     and expect it to function as it was designed to do.
105. The third and fourth principles result from the complexity of human
     societies. A change in human behavior will affect the economy of a society
     and its physical environment; the economy will affect the environment and
     vice versa, and the changes in the economy and the environment will affect
     human behavior in complex, unpredictable ways; and so forth. The network of
     causes and effects is far tooM
 complex to be untangled and understood.
106. FIFTH PRINCIPLE. People do not consciously and rationally choose the form
     of their society. Societies develop through processes of social evolution
     that are not under rational human control.
107. The fifth principle is a consequence of the other four.
108. To illustrate: By the first principle, generally speaking an attempt at
     social reform either acts in the direction in which the society is
     developing anyway (so that it merely accelerates a M
change that would have
     occurred in any case) or else it has only a transitory effect, so that the
     society soon slips back into its old groove. To make a lasting change in
     the direction of development of any important aspect of a society, reform
     is insufficient and revolution is required. (A revolution does not
     necessarily involve an armed uprising or the overthrow of a government.) By
     the second principle, a revolution never changes only one aspect of
     a society, it changes M
the whole society; and by the third principle changes
     occur that were never expected or desired by the revolutionaries. By the
     fourth principle, when revolutionaries or utopians set up a new kind of
     society, it never works out as planned.
109. The American Revolution does not provide a counterexample. The American
 was not a revolution in our sense of the word, but a war of
     independence followed by a rather far-reaching political reform. The
     Founding Fathers did M
not change the direction of development of American
     society, nor did they aspire to do so. They only freed the development of
     American society from the retarding effect of British rule. Their political
     reform did not change any basic trend, but only pushed American political
     culture along its natural direction of development. British society, of
     which American society was an offshoot, had been moving for a long time in
     the direction of representative democracy. And prior to the WM
     Independence the Americans were already practicing a significant degree of
     representative democracy in the colonial assemblies. The political system
     established by the Constitution was modeled on the British system and on
     the colonial assemblies. With major alteration, to be sure
     doubt that the Founding Fathers took a very important step. But it was
     a step along the road that English-speaking world was already traveling.
     The proof is that Britain and allM
 of its colonies that were populated
     predominantly by people of British descent ended up with systems of
     representative democracy essentially similar to that of the United States.
     If the Founding Fathers had lost their nerve and declined to sign the
     Declaration of Independence, our way of life today would not have been
     significantly different. Maybe we would have had somewhat closer ties to
     Britain, and would have had a Parliament and Prime Minister instead of
d President. No big deal. Thus the American Revolution
     provides not a counterexample to our principles but a good illustration of
110. Still, one has to use common sense in applying the principles. They are
     expressed in imprecise language that allows latitude for interpretation,
     and exceptions to them can be found. So we present these principles not as
     inviolable laws but as rules of thumb, or guides to thinking, that may
     provide a partial antidote to naive ideas about theM
 future of society. The
     principles should be borne constantly in mind, and whenever one reaches
     a conclusion that conflicts with them one should carefully reexamine one
     thinking and retain the conclusion only if one has good, solid reasons for
    INDUSTRIAL-TECHNOLOGICAL SOCIETY CANNOT BE REFORMED
111. The foregoing principles help to show how hopelessly difficult it would be
     to reform the industrial system in such a way as to prevent it from
     progressively narrowM
ing our sphere of freedom. There has been a consistent
     tendency, going back at least to the Industrial Revolution for technology
     to strengthen the system at a high cost in individual freedom and local
     autonomy. Hence any change designed to protect freedom from technology
     would be contrary to a fundamental trend in the development of our society.
     Consequently, such a change either would be a transitory one
     by the tide of history
or, if large enough to be permanentM
     nature of our whole society. This by the first and second principles.
     Moreover, since society would be altered in a way that could not be
     predicted in advance (third principle) there would be great risk. Changes
     large enough to make a lasting difference in favor of freedom would not be
     initiated because it would be realized that they would gravely disrupt the
     system. So any attempts at reform would be too timid to be effective. Even
     if changes large enough M
to make a lasting difference were initiated, they
     would be retracted when their disruptive effects became apparent. Thus,
     permanent changes in favor of freedom could be brought about only by
     persons prepared to accept radical, dangerous and unpredictable alteration
     of the entire system. In other words by revolutionaries, not reformers.
112. People anxious to rescue freedom without sacrificing the supposed benefits
     of technology will suggest naive schemes for some new form of society tM
     would reconcile freedom with technology. Apart from the fact that people
     who make such suggestions seldom propose any practical means by which the
     new form of society could be set up in the first place, it follows from the
     fourth principle that even if the new form of society could be once
     established, it either would collapse or would give results very different
     from those expected.
113. So even on very general grounds it seems highly improbable that any way of
ng society could be found that would reconcile freedom with modern
     technology. In the next few sections we will give more specific reasons for
     concluding that freedom and technological progress are incompatible.
RESTRICTION OF FREEDOM IS UNAVOIDABLE IN INDUSTRIAL SOCIETY
114. As explained in paragraphs 65-67, 70-73, modern man is strapped down by
     a network of rules and regulations, and his fate depends on the actions of
     persons remote from him whose decisions he cannot influence. This is nM
     accidental or a result of the arbitrariness of arrogant bureaucrats. It is
     necessary and inevitable in any technologically advanced society. The
     system HAS TO regulate human behavior closely in order to function. At work
     people have to do what they are told to do, otherwise production would be
     thrown into chaos. Bureaucracies HAVE TO be run according to rigid rules.
     To allow any substantial personal discretion to lower-level bureaucrats
     would disrupt the system and lead M
to charges of unfairness due to
     differences in the way individual bureaucrats exercised their discretion.
     It is true that some restrictions on our freedom could be eliminated, but
     GENERALLY SPEAKING the regulation of our lives by large organizations is
     necessary for the functioning of industrial-technological society. The
     result is a sense of powerlessness on the part of the average person. It
     may be, however, that formal regulations will tend increasingly to be
 psychological tools that make us want to do what the system
     requires of us. (Propaganda [14], educational techniques,
     programs, etc.)
115. The system HAS TO force people to behave in ways that are increasingly
     remote from the natural pattern of human behavior. For example, the system
     needs scientists, mathematicians and engineers. It can
     them. So heavy pressure is put on children to excel in these fields. It
t natural for an adolesM
cent human being to spend the bulk of his time
     sitting at a desk absorbed in study. A normal adolescent wants to spend his
     time in active contact with the real world. Among primitive peoples the
     things that children are trained to do tend to be in reasonable harmony
     with natural human impulses. Among the American Indians, for example, boys
     were trained in active outdoor pursuits
just the sort of thing that boys
     like. But in our society children are pushed into studying technicaM
     subjects, which most do grudgingly.
116. Because of the constant pressure that the system exerts to modify human
     behavior, there is a gradual increase in the number of people who cannot or
     will not adjust to society
s requirements: welfare leeches, youth-gang
     members, cultists, anti-government rebels, radical environmentalist
     saboteurs, dropouts and resisters of various kinds.
117. In any technologically advanced society the individual
     on decisions thatM
 he personally cannot influence to any great extent.
     A technological society cannot be broken down into small, autonomous
     communities, because production depends on the cooperation of very large
     numbers of people and machines. Such a society MUST be highly organized and
     decisions HAVE TO be made that affect very large numbers of people. When
     a decision affects, say, a million people, then each of the affected
     individuals has, on the average, only a one-millionth share in making tM
     decision. What usually happens in practice is that decisions are made by
     public officials or corporation executives, or by technical specialists,
     but even when the public votes on a decision the number of voters
     ordinarily is too large for the vote of any one individual to be
     significant. [17] Thus most individuals are unable to influence measurably
     the major decisions that affect their lives. There is no conceivable way to
     remedy this in a technologically advanced socieM
ty. The system tries to
 this problem by using propaganda to make people WANT the decisions
     that have been made for them, but even if this
     successful in making people feel better, it would be demeaning.
118. Conservatives and some others advocate more
     communities once did have autonomy, but such autonomy becomes less and less
     possible as local communities become more enmeshed with and dependent on
systems like public utilities, computer networks, highway
     systems, the mass communications media, the modern health care system. Also
     operating against autonomy is the fact that technology applied in one
     location often affects people at other locations far way. Thus pesticide or
     chemical use near a creek may contaminate the water supply hundreds of
     miles downstream, and the greenhouse effect affects the whole world.
119. The system does not and cannot exist to satisfy human needs. InsM
     human behavior that has to be modified to fit the needs of the system. This
     has nothing to do with the political or social ideology that may pretend to
     guide the technological system. It is the fault of technology, because the
     system is guided not by ideology but by technical necessity. [18] Of course
     the system does satisfy many human needs, but generally speaking it does
     this only to the extend that it is to the advantage of the system to do it.
     It is the needM
s of the system that are paramount, not those of the human
     being. For example, the system provides people with food because the system
t function if everyone starved; it attends to people
     needs whenever it can CONVENIENTLY do so, because it couldn
     too many people became depressed or rebellious. But the system, for good,
     solid, practical reasons, must exert constant pressure on people to mold
     their behavior to the needs of the system. To M
much waste accumulating? The
     government, the media, the educational system, environmentalists, everyone
     inundates us with a mass of propaganda about recycling. Need more technical
     personnel? A chorus of voices exhorts kids to study science. No one stops
     to ask whether it is inhumane to force adolescents to spend the bulk of
     their time studying subjects most of them hate. When skilled workers are
     put out of a job by technical advances and have to undergo
 one asks whether it is humiliating for them to be pushed around in this
     way. It is simply taken for granted that everyone must bow to technical
     necessity. and for good reason: If human needs were put before technical
     necessity there would be economic problems, unemployment, shortages or
     worse. The concept of
 in our society is defined largely by
     the extent to which an individual behaves in accord with the needs of the
     system and does so without showing signs oM
120. Efforts to make room for a sense of purpose and for autonomy within the
     system are no better than a joke. For example, one company, instead of
     having each of its employees assemble only one section of a catalogue, had
     each assemble a whole catalogue, and this was supposed to give them a sense
     of purpose and achievement. Some companies have tried to give their
     employees more autonomy in their work, but for practical reasons this
     usually can be done only to a very lM
imited extent, and in any case
     employees are never given autonomy as to ultimate goals
     efforts can never be directed toward goals that they select personally, but
     only toward their employer
s goals, such as the survival and growth of the
     company. Any company would soon go out of business if it permitted its
     employees to act otherwise. Similarly, in any enterprise within a socialist
     system, workers must direct their efforts toward the goals of the
enterprise, otherwise the enterprise will not serve its purpose as part of
     the system. Once again, for purely technical reasons it is not possible for
     most individuals or small groups to have much autonomy in industrial
     society. Even the small-business owner commonly has only limited autonomy.
     Apart from the necessity of government regulation, he is restricted by the
     fact that he must fit into the economic system and conform to its
     requirements. For instance, when someone developM
s a new technology, the
     small-business person often has to use that technology whether he wants to
     or not, in order to remain competitive.
 PARTS OF TECHNOLOGY CANNOT BE SEPARATED FROM THE
121. A further reason why industrial society cannot be reformed in favor of
     freedom is that modern technology is a unified system in which all parts
     are dependent on one another. You can
     technology and retain only the
rts. Take modern medicine, for
     example. Progress in medical science depends on progress in chemistry,
     physics, biology, computer science and other fields. Advanced medical
     treatments require expensive, high-tech equipment that can be made
     available only by a technologically progressive, economically rich society.
     Clearly you can
t have much progress in medicine without the whole
     technological system and everything that goes with it.
122. Even if medical progress could be maintM
ained without the rest of the
     technological system, it would by itself bring certain evils. Suppose for
     example that a cure for diabetes is discovered. People with a genetic
     tendency to diabetes will then be able to survive and reproduce as well as
     anyone else. Natural selection against genes for diabetes will cease and
     such genes will spread throughout the population. (This may be occurring to
     some extent already, since diabetes, while not curable, can be controlled
h use of insulin.) The same thing will happen with many other
     diseases susceptibility to which is affected by genetic degradation of the
     population. The only solution will be some sort of eugenics program or
     extensive genetic engineering of human beings, so that man in the future
     will no longer be a creation of nature, or of chance, or of God (depending
     on your religious or philosophical opinions), but a manufactured product.
123. If you think that big government interferes in your liM
fe too much NOW, just
     wait till the government starts regulating the genetic constitution of your
     children. Such regulation will inevitably follow the introduction of
     genetic engineering of human beings, because the consequences of
     unregulated genetic engineering would be disastrous. [19]
124. The usual response to such concerns is to talk about
     a code of ethics would not serve to protect freedom in the face of medical
     progress; it would only make matteM
rs worse. A code of ethics applicable to
     genetic engineering would be in effect a means of regulating the genetic
     constitution of human beings. Somebody (probably the upper-middle class,
     mostly) would decide that such and such applications of genetic engineering
 and others were not, so that in effect they would be
     imposing their own values on the genetic constitution of the population at
     large. Even if a code of ethics were chosen on a completely democratic
   basis, the majority would be imposing their own values on any minorities
     who might have a different idea of what constituted an
     genetic engineering. The only code of ethics that would truly protect
     freedom would be one that prohibited ANY genetic engineering of human
     beings, and you can be sure that no such code will ever be applied in
     a technological society. No code that reduced genetic engineering to
     a minor role could stand up for long, because the temM
ptation presented by
     the immense power of biotechnology would be irresistible, especially since
     to the majority of people many of its applications will seem obviously and
     unequivocally good (eliminating physical and mental diseases, giving people
     the abilities they need to get along in today
s world). Inevitably, genetic
     engineering will be used extensively, but only in ways consistent with the
     needs of the industrial- technological system. [20]
TECHNOLOGY IS A MORE POWERFUL SM
OCIAL FORCE THAN THE ASPIRATION FOR FREEDOM
125. It is not possible to make a LASTING compromise between technology and
     freedom, because technology is by far the more powerful social force and
     continually encroaches on freedom through REPEATED compromises. Imagine the
     case of two neighbors, each of whom at the outset owns the same amount of
     land, but one of whom is more powerful than the other. The powerful one
     demands a piece of the other
s land. The weak one refuses. The powerful M
s compromise. Give me half of what I asked.
     has little choice but to give in. Some time later the powerful neighbor
     demands another piece of land, again there is a compromise, and so forth.
     By forcing a long series of compromises on the weaker man, the powerful one
     eventually gets all of his land. So it goes in the conflict between
     technology and freedom.
126. Let us explain why technology is a more powerful social force than the
127. A technological advance that appears not to threaten freedom often turns
     out to threaten it very seriously later on. For example, consider motorized
     transport. A walking man formerly could go where he pleased, go at his own
     pace without observing any traffic regulations, and was independent of
     technological support-systems. When motor vehicles were introduced they
     appeared to increase man
s freedom. They took no freedom away from the
     walking man, no onM
e had to have an automobile if he didn
     anyone who did choose to buy an automobile could travel much faster and
     farther than a walking man. But the introduction of motorized transport
     soon changed society in such a way as to restrict greatly man
     locomotion. When automobiles became numerous, it became necessary to
     regulate their use extensively. In a car, especially in densely populated
     areas, one cannot just go where one likes at one
     is governed by the flow of traffic and by various traffic laws. One is tied
     down by various obligations: license requirements, driver test, renewing
     registration, insurance, maintenance required for safety, monthly payments
     on purchase price. Moreover, the use of motorized transport is no longer
     optional. Since the introduction of motorized transport the arrangement of
     our cities has changed in such a way that the majority of people no longer
     live within walM
king distance of their place of employment, shopping areas
     and recreational opportunities, so that they HAVE TO depend on the
     automobile for transportation. Or else they must use public transportation,
     in which case they have even less control over their own movement than when
     driving a car. Even the walker
s freedom is now greatly restricted. In the
     city he continually has to stop to wait for traffic lights that are
     designed mainly to serve auto traffic. In the country, motor M
     it dangerous and unpleasant to walk along the highway. (Note this important
     point that we have just illustrated with the case of motorized transport:
     When a new item of technology is introduced as an option that an individual
     can accept or not as he chooses, it does not necessarily REMAIN optional.
     In many cases the new technology changes society in such a way that people
     eventually find themselves FORCED to use it.)
128. While technological progress AS A WHOLE conM
tinually narrows our sphere of
     freedom, each new technical advance CONSIDERED BY ITSELF appears to be
     desirable. Electricity, indoor plumbing, rapid long-distance communications
     ... how could one argue against any of these things, or against any other
     of the innumerable technical advances that have made modern society? It
     would have been absurd to resist the introduction of the telephone, for
     example. It offered many advantages and no disadvantages. Yet, as we
paragraphs 59-76, all these technical advances taken together
     have created a world in which the average man
s fate is no longer in his
     own hands or in the hands of his neighbors and friends, but in those of
     politicians, corporation executives and remote, anonymous technicians and
     bureaucrats whom he as an individual has no power to influence. [21] The
     same process will continue in the future. Take genetic engineering, for
     example. Few people will resist the introduction of a geM
     that eliminates a hereditary disease. It does no apparent harm and prevents
     much suffering. Yet a large number of genetic improvements taken together
     will make the human being into an engineered product rather than a free
     creation of chance (or of God, or whatever, depending on your religious
129. Another reason why technology is such a powerful social force is that,
     within the context of a given society, technological progress marches in
 direction; it can never be reversed. Once a technical innovation
     has been introduced, people usually become dependent on it, so that they
     can never again do without it, unless it is replaced by some still more
     advanced innovation. Not only do people become dependent as individuals on
     a new item of technology, but, even more, the system as a whole becomes
     dependent on it. (Imagine what would happen to the system today if
     computers, for example, were eliminated.) Thus the system cM
     one direction, toward greater technologization. Technology repeatedly
     forces freedom to take a step back, but technology can never take a step
short of the overthrow of the whole technological system.
130. Technology advances with great rapidity and threatens freedom at many
     different points at the same time (crowding, rules and regulations,
     increasing dependence of individuals on large organizations, propaganda and
     other psychological techniques, genetic M
engineering, invasion of privacy
     through surveillance devices and computers, etc.). To hold back any ONE of
     the threats to freedom would require a long and difficult social struggle.
     Those who want to protect freedom are overwhelmed by the sheer number of
     new attacks and the rapidity with which they develop, hence they become
     apathetic and no longer resist. To fight each of the threats separately
     would be futile. Success can be hoped for only by fighting the
     technological sM
ystem as a whole; but that is revolution, not reform.
131. Technicians (we use this term in its broad sense to describe all those who
     perform a specialized task that requires training) tend to be so involved
     in their work (their surrogate activity) that when a conflict arises
     between their technical work and freedom, they almost always decide in
     favor of their technical work. This is obvious in the case of scientists,
     but it also appears elsewhere: Educators, humanitarian groups, consM
     organizations do not hesitate to use propaganda or other psychological
     techniques to help them achieve their laudable ends. Corporations and
     government agencies, when they find it useful, do not hesitate to collect
     information about individuals without regard to their privacy. Law
     enforcement agencies are frequently inconvenienced by the constitutional
     rights of suspects and often of completely innocent persons, and they do
     whatever they can do legally (or sometimeM
s illegally) to restrict or
     circumvent those rights. Most of these educators, government officials and
     law officers believe in freedom, privacy and constitutional rights, but
     when these conflict with their work, they usually feel that their work is
     more important.
132. It is well known that people generally work better and more persistently
     when striving for a reward than when attempting to avoid a punishment or
     negative outcome. Scientists and other technicians are motivated maM
     the rewards they get through their work. But those who oppose technological
     invasions of freedom are working to avoid a negative outcome, consequently
     there are few who work persistently and well at this discouraging task. If
     reformers ever achieved a signal victory that seemed to set up a solid
     barrier against further erosion of freedom through technical progress, most
     would tend to relax and turn their attention to more agreeable pursuits.
     But the scientists wouldM
 remain busy in their laboratories, and technology
     as it progresses would find ways, in spite of any barriers, to exert more
     and more control over individuals and make them always more dependent on
133. No social arrangements, whether laws, institutions, customs or ethical
     codes, can provide permanent protection against technology. History shows
     that all social arrangements are transitory; they all change or break down
     eventually. But technological advances are permaM
nent within the context of
     a given civilization. Suppose for example that it were possible to arrive
     at some social arrangements that would prevent genetic engineering from
     being applied to human beings, or prevent it from being applied in such
     a way as to threaten freedom and dignity. Still, the technology would
     remain waiting. Sooner or later the social arrangement would break down.
     Probably sooner, given the pace of change in our society. Then genetic
     engineering would bM
egin to invade our sphere of freedom, and this invasion
     would be irreversible (short of a breakdown of technological civilization
     itself). Any illusions about achieving anything permanent through social
     arrangements should be dispelled by what is currently happening with
     environmental legislation. A few years ago its seemed that there were
     secure legal barriers preventing at least SOME of the worst forms of
     environmental degradation. A change in the political wind, and those
  barriers begin to crumble.
134. For all of the foregoing reasons, technology is a more powerful social
     force than the aspiration for freedom. But this statement requires an
     important qualification. It appears that during the next several decades
     the industrial-technological system will be undergoing severe stresses due
     to economic and environmental problems, and especially due to problems of
     human behavior (alienation, rebellion, hostility, a variety of social and
l difficulties). We hope that the stresses through which the
     system is likely to pass will cause it to break down, or at least will
     weaken it sufficiently so that a revolution against it becomes possible. If
     such a revolution occurs and is successful, then at that particular moment
     the aspiration for freedom will have proved more powerful than technology.
135. In paragraph 125 we used an analogy of a weak neighbor who is left
     destitute by a strong neighbor who takes all his land by foM
     a series of compromises. But suppose now that the strong neighbor gets
     sick, so that he is unable to defend himself. The weak neighbor can force
     the strong one to give him his land back, or he can kill him. If he lets
     the strong man survive and only forces him to give the land back, he is
     a fool, because when the strong man gets well he will again take all the
     land for himself. The only sensible alternative for the weaker man is to
     kill the strong one while he M
has the chance. In the same way, while the
     industrial system is sick we must destroy it. If we compromise with it and
     let it recover from its sickness, it will eventually wipe out all of our
SIMPLER SOCIAL PROBLEMS HAVE PROVED INTRACTABLE
136. If anyone still imagines that it would be possible to reform the system in
     such a way as to protect freedom from technology, let him consider how
     clumsily and for the most part unsuccessfully our society has dealt with
al problems that are far more simple and straightforward. Among
     other things, the system has failed to stop environmental degradation,
     political corruption, drug trafficking or domestic abuse.
137. Take our environmental problems, for example. Here the conflict of values
     is straightforward: economic expedience now versus saving some of our
     natural resources for our grandchildren. [22] But on this subject we get
     only a lot of blather and obfuscation from the people who have power, and M
     nothing like a clear, consistent line of action, and we keep on piling up
     environmental problems that our grandchildren will have to live with.
     Attempts to resolve the environmental issue consist of struggles and
     compromises between different factions, some of which are ascendant at one
     moment, others at another moment. The line of struggle changes with the
     shifting currents of public opinion. This is not a rational process, nor is
     it one that is likely to lead to a timely M
and successful solution to the
     problem. Major social problems, if they get
 at all, are rarely or
     never solved through any rational, comprehensive plan. They just work
     themselves out through a process in which various competing groups pursuing
     their own (usually short- term) self-interest [23] arrive (mainly by luck)
     at some more or less stable modus vivendi. In fact, the principles we
     formulated in paragraphs 100-106 make it seem doubtful that rational,
rm social planning can EVER be successful.
138. Thus it is clear that the human race has at best a very limited capacity
     for solving even relatively straightforward social problems. How then is it
     going to solve the far more difficult and subtle problem of reconciling
     freedom with technology? Technology presents clear-cut material advantages,
     whereas freedom is an abstraction that means different things to different
     people, and its loss is easily obscured by propaganda and fancy talk.M
139. And note this important difference: It is conceivable that our
     environmental problems (for example) may some day be settled through
     a rational, comprehensive plan, but if this happens it will be only because
     it is in the long-term interest of the system to solve these problems. But
     it is NOT in the interest of the system to preserve freedom or small-group
     autonomy. On the contrary, it is in the interest of the system to bring
     human behavior under control to the greatest poM
ssible extent. [24] Thus,
     while practical considerations may eventually force the system to take
     a rational, prudent approach to environmental problems, equally practical
     considerations will force the system to regulate human behavior ever more
     closely (preferably by indirect means that will disguise the encroachment
     on freedom). This isn
t just our opinion. Eminent social scientists (e.g.
     James Q. Wilson) have stressed the importance of
REVOLUTION IS EASIER THAN REFORM
140. We hope we have convinced the reader that the system cannot be reformed in
     such a way as to reconcile freedom with technology. The only way out is to
     dispense with the industrial-technological system altogether. This implies
     revolution, not necessarily an armed uprising, but certainly a radical and
     fundamental change in the nature of society.
141. People tend to assume that because a revolution involves a much greater
     change than reformM
 does, it is more difficult to bring about than reform
     is. Actually, under certain circumstances revolution is much easier than
     reform. The reason is that a revolutionary movement can inspire an
     intensity of commitment that a reform movement cannot inspire. A reform
     movement merely offers to solve a particular social problem.
     A revolutionary movement offers to solve all problems at one stroke and
     create a whole new world; it provides the kind of ideal for which people
take great risks and make great sacrifices. For this reasons it would
     be much easier to overthrow the whole technological system than to put
     effective, permanent restraints on the development or application of any
     one segment of technology, such as genetic engineering, for example. Not
     many people will devote themselves with single-minded passion to imposing
     and maintaining restraints on genetic engineering, but under suitable
     conditions large numbers of people may devote themselM
ves passionately to
     a revolution against the industrial-technological system. As we noted in
     paragraph 132, reformers seeking to limit certain aspects of technology
     would be working to avoid a negative outcome. But revolutionaries work to
     gain a powerful reward
fulfillment of their revolutionary vision
     therefore work harder and more persistently than reformers do.
142. Reform is always restrained by the fear of painful consequences if changes
     go too far. But once a revolM
utionary fever has taken hold of a society,
     people are willing to undergo unlimited hardships for the sake of their
     revolution. This was clearly shown in the French and Russian Revolutions.
     It may be that in such cases only a minority of the population is really
     committed to the revolution, but this minority is sufficiently large and
     active so that it becomes the dominant force in society. We will have more
     to say about revolution in paragraphs 180-205.
CONTROL OF HUMAN BEHAVIORM
143. Since the beginning of civilization, organized societies have had to put
     pressures on human beings of the sake of the functioning of the social
     organism. The kinds of pressures vary greatly from one society to another.
     Some of the pressures are physical (poor diet, excessive labor,
     environmental pollution), some are psychological (noise, crowding, forcing
     human behavior into the mold that society requires). In the past, human
     nature has been approximately constant, or at aM
ny rate has varied only
     within certain bounds. Consequently, societies have been able to push
     people only up to certain limits. When the limit of human endurance has
     been passed, things start going wrong: rebellion, or crime, or corruption,
     or evasion of work, or depression and other mental problems, or an elevated
     death rate, or a declining birth rate or something else, so that either the
     society breaks down, or its functioning becomes too inefficient and it is
 gradually, through conquest, attrition or evolution) replaced
     by some more efficient form of society. [25]
144. Thus human nature has in the past put certain limits on the development of
     societies. People could be pushed only so far and no farther. But today
     this may be changing, because modern technology is developing ways of
     modifying human beings.
145. Imagine a society that subjects people to conditions that make them
     terribly unhappy, then gives them drugs to take away their unM
     Science fiction? It is already happening to some extent in our own society.
     It is well known that the rate of clinical depression has been greatly
     increasing in recent decades. We believe that this is due to disruption of
     the power process, as explained in paragraphs 59-76. But even if we are
     wrong, the increasing rate of depression is certainly the result of SOME
     conditions that exist in today
s society. Instead of removing the
     conditions that make people deprM
essed, modern society gives them
     antidepressant drugs. In effect, antidepressants are a means of modifying
s internal state in such a way as to enable him to tolerate
     social conditions that he would otherwise find intolerable. (Yes, we know
     that depression is often of purely genetic origin. We are referring here to
     those cases in which environment plays the predominant role.)
146. Drugs that affect the mind are only one example of the new methods of
uman behavior that modern society is developing. Let us look
     at some of the other methods.
147. To start with, there are the techniques of surveillance. Hidden video
     cameras are now used in most stores and in many other places, computers are
     used to collect and process vast amounts of information about individuals.
     Information so obtained greatly increases the effectiveness of physical
     coercion (i.e., law enforcement). [26] Then there are the methods of
     propaganda, for which theM
 mass communication media provide effective
     vehicles. Efficient techniques have been developed for winning elections,
     selling products, influencing public opinion. The entertainment industry
     serves as an important psychological tool of the system, possibly even when
     it is dishing out large amounts of sex and violence. Entertainment provides
     modern man with an essential means of escape. While absorbed in television,
     videos, etc., he can forget stress, anxiety, frustration, dissatiM
     Many primitive peoples, when they don
t have work to do, are quite content
     to sit for hours at a time doing nothing at all, because they are at peace
     with themselves and their world. But most modern people must be constantly
     occupied or entertained, otherwise they get
     fidgety, uneasy, irritable.
148. Other techniques strike deeper than the foregoing. Education is no longer
     a simple affair of paddling a kid
s behind when he doesn
     and patting him on the head when he does know them. It is becoming
     a scientific technique for controlling the child
s development. Sylvan
     Learning Centers, for example, have had great success in motivating
     children to study, and psychological techniques are also used with more or
     less success in many conventional schools.
 techniques that are
     taught to parents are designed to make children accept fundamental values
     of the system and behave in wM
ays that the system finds desirable.
 techniques, psychotherapy and so forth are
     ostensibly designed to benefit individuals, but in practice they usually
     serve as methods for inducing individuals to think and behave as the system
     requires. (There is no contradiction here; an individual whose attitudes or
     behavior bring him into conflict with the system is up against a force that
     is too powerful for him to conquer or escape from, henM
     suffer from stress, frustration, defeat. His path will be much easier if he
     thinks and behaves as the system requires. In that sense the system is
     acting for the benefit of the individual when it brainwashes him into
     conformity.) Child abuse in its gross and obvious forms is disapproved in
     most if not all cultures. Tormenting a child for a trivial reason or no
     reason at all is something that appalls almost everyone. But many
     psychologists interpret the coM
ncept of abuse much more broadly. Is
     spanking, when used as part of a rational and consistent system of
     discipline, a form of abuse? The question will ultimately be decided by
     whether or not spanking tends to produce behavior that makes a person fit
     in well with the existing system of society. In practice, the word
     tends to be interpreted to include any method of child-rearing that
     produces behavior inconvenient for the system. Thus, when they go beyond
ention of obvious, senseless cruelty, programs for preventing
 are directed toward the control of human behavior on behalf
149. Presumably, research will continue to increase the effectiveness of
     psychological techniques for controlling human behavior. But we think it is
     unlikely that psychological techniques alone will be sufficient to adjust
     human beings to the kind of society that technology is creating. Biological
     methods probably will have toM
 be used. We have already mentioned the use of
     drugs in this connection. Neurology may provide other avenues for modifying
     the human mind. Genetic engineering of human beings is already beginning to
     occur in the form of
 and there is no reason to assume that
     such methods will not eventually be used to modify those aspects of the
     body that affect mental functioning.
150. As we mentioned in paragraph 134, industrial society seems likely to be
     entering a period oM
f severe stress, due in part to problems of human
     behavior and in part to economic and environmental problems. And
     a considerable proportion of the system
s economic and environmental
     problems result from the way human beings behave. Alienation, low
     self-esteem, depression, hostility, rebellion; children who won
     youth gangs, illegal drug use, rape, child abuse, other crimes, unsafe sex,
     teen pregnancy, population growth, political corruption, race hatred,
c rivalry, bitter ideological conflict (e.g., pro-choice vs. pro-
     life), political extremism, terrorism, sabotage, anti-government groups,
     hate groups. All these threaten the very survival of the system. The system
     will therefore be FORCED to use every practical means of controlling human
151. The social disruption that we see today is certainly not the result of mere
     chance. It can only be a result of the conditions of life that the system
     imposes on people. (We have M
argued that the most important of these
     conditions is disruption of the power process.) If the systems succeeds in
     imposing sufficient control over human behavior to assure its own survival,
     a new watershed in human history will have been passed. Whereas formerly
     the limits of human endurance have imposed limits on the development of
     societies (as we explained in paragraphs 143, 144),
     industrial-technological society will be able to pass those limits by
     modifying human beinM
gs, whether by psychological methods or biological
     methods or both. In the future, social systems will not be adjusted to suit
     the needs of human beings. Instead, human being will be adjusted to suit
     the needs of the system. [27]
152. Generally speaking, technological control over human behavior will probably
     not be introduced with a totalitarian intention or even through a conscious
     desire to restrict human freedom. [28] Each new step in the assertion of
     control over the human M
mind will be taken as a rational response to
     a problem that faces society, such as curing alcoholism, reducing the crime
     rate or inducing young people to study science and engineering. In many
     cases there will be a humanitarian justification. For example, when
     a psychiatrist prescribes an anti-depressant for a depressed patient, he is
     clearly doing that individual a favor. It would be inhumane to withhold the
     drug from someone who needs it. When parents send their children to SylM
     Learning Centers to have them manipulated into becoming enthusiastic about
     their studies, they do so from concern for their children
     be that some of these parents wish that one didn
t have to have specialized
     training to get a job and that their kid didn
t have to be brainwashed into
     becoming a computer nerd. But what can they do? They can
     and their child may be unemployable if he doesn
t have certain skills. So
153. Thus control over human behavior will be introduced not by a calculated
     decision of the authorities but through a process of social evolution
     (RAPID evolution, however). The process will be impossible to resist,
     because each advance, considered by itself, will appear to be beneficial,
     or at least the evil involved in making the advance will appear to be
     beneficial, or at least the evil involved in making the advance will seem
     to be less than that which would reM
sult from not making it (see paragraph
     127). Propaganda for example is used for many good purposes, such as
     discouraging child abuse or race hatred. [14] Sex education is obviously
     useful, yet the effect of sex education (to the extent that it is
     successful) is to take the shaping of sexual attitudes away from the family
     and put it into the hands of the state as represented by the public school
154. Suppose a biological trait is discovered that increases the likelihood tM
     a child will grow up to be a criminal, and suppose some sort of gene
     therapy can remove this trait. [29] Of course most parents whose children
     possess the trait will have them undergo the therapy. It would be inhumane
     to do otherwise, since the child would probably have a miserable life if he
     grew up to be a criminal. But many or most primitive societies have a low
     crime rate in comparison with that of our society, even though they have
     neither high- tech methods of chiM
ld-rearing nor harsh systems of
     punishment. Since there is no reason to suppose that more modern men than
     primitive men have innate predatory tendencies, the high crime rate of our
     society must be due to the pressures that modern conditions put on people,
     to which many cannot or will not adjust. Thus a treatment designed to
     remove potential criminal tendencies is at least in part a way of
     re-engineering people so that they suit the requirements of the system.
155. Our society teM
 any mode of thought or behavior
     that is inconvenient for the system, and this is plausible because when an
     individual doesn
t fit into the system it causes pain to the individual as
     well as problems for the system. Thus the manipulation of an individual to
     adjust him to the system is seen as a
156. In paragraph 127 we pointed out that if the use of a new item of technology
     is INITIALLY optM
ional, it does not necessarily REMAIN optional, because the
     new technology tends to change society in such a way that it becomes
     difficult or impossible for an individual to function without using that
     technology. This applies also to the technology of human behavior. In
     a world in which most children are put through a program to make them
     enthusiastic about studying, a parent will almost be forced to put his kid
     through such a program, because if he does not, then the kid will gM
     to be, comparatively speaking, an ignoramus and therefore unemployable. Or
     suppose a biological treatment is discovered that, without undesirable
     side-effects, will greatly reduce the psychological stress from which so
     many people suffer in our society. If large numbers of people choose to
     undergo the treatment, then the general level of stress in society will be
     reduced, so that it will be possible for the system to increase the
     stress-producing pressures. In fact, M
something like this seems to have
     happened already with one of our society
s most important psychological
     tools for enabling people to reduce (or at least temporarily escape from)
     stress, namely, mass entertainment (see paragraph 147). Our use of mass
     entertainment is
: No law requires us to watch television, listen
     to the radio, read magazines. Yet mass entertainment is a means of escape
     and stress-reduction on which most of us have become dependent. Everyone
   complains about the trashiness of television, but almost everyone watches
     it. A few have kicked the TV habit, but it would be a rare person who could
     get along today without using ANY form of mass entertainment. (Yet until
     quite recently in human history most people got along very nicely with no
     other entertainment than that which each local community created for
     itself.) Without the entertainment industry the system probably would not
     have been able to get away with putting aM
s much stress-producing pressure
     on us as it does.
157. Assuming that industrial society survives, it is likely that technology
     will eventually acquire something approaching complete control over human
     behavior. It has been established beyond any rational doubt that human
     thought and behavior have a largely biological basis. As experimenters have
     demonstrated, feelings such as hunger, pleasure, anger and fear can be
     turned on and off by electrical stimulation of appropriate partM
     brain. Memories can be destroyed by damaging parts of the brain or they can
     be brought to the surface by electrical stimulation. Hallucinations can be
     induced or moods changed by drugs. There may or may not be an immaterial
     human soul, but if there is one it clearly is less powerful that the
     biological mechanisms of human behavior. For if that were not the case then
     researchers would not be able so easily to manipulate human feelings and
     behavior with drugs and eleM
158. It presumably would be impractical for all people to have electrodes
     inserted in their heads so that they could be controlled by the
     authorities. But the fact that human thoughts and feelings are so open to
     biological intervention shows that the problem of controlling human
     behavior is mainly a technical problem; a problem of neurons, hormones and
     complex molecules; the kind of problem that is accessible to scientific
     attack. Given the outstanding record oM
f our society in solving technical
     problems, it is overwhelmingly probable that great advances will be made in
     the control of human behavior.
159. Will public resistance prevent the introduction of technological control of
     human behavior? It certainly would if an attempt were made to introduce
     such control all at once. But since technological control will be
     introduced through a long sequence of small advances, there will be no
     rational and effective public resistance. (See paraM
graphs 127, 132, 153.)
160. To those who think that all this sounds like science fiction, we point out
s science fiction is today
s fact. The Industrial Revolution
     has radically altered man
s environment and way of life, and it is only to
     be expected that as technology is increasingly applied to the human body
     and mind, man himself will be altered as radically as his environment and
     way of life have been.
HUMAN RACE AT A CROSSROADS
161. But we have gotten ahead M
of our story. It is one thing to develop in the
     laboratory a series of psychological or biological techniques for
     manipulating human behavior and quite another to integrate these techniques
     into a functioning social system. The latter problem is the more difficult
     of the two. For example, while the techniques of educational psychology
     doubtless work quite well in the
 where they are developed, it
     is not necessarily easy to apply them effectively throughout our
    educational system. We all know what many of our schools are like. The
     teachers are too busy taking knives and guns away from the kids to subject
     them to the latest techniques for making them into computer nerds. Thus, in
     spite of all its technical advances relating to human behavior, the system
     to date has not been impressively successful in controlling human beings.
     The people whose behavior is fairly well under the control of the system
     are those of the type that might be M
     growing numbers of people who in one way or another are rebels against the
     system: welfare leaches, youth gangs, cultists, satanists, nazis, radical
     environmentalists, militiamen, etc.
162. The system is currently engaged in a desperate struggle to overcome certain
     problems that threaten its survival, among which the problems of human
     behavior are the most important. If the system succeeds in acquiring
     sufficient control over human behavior M
quickly enough, it will probably
     survive. Otherwise it will break down. We think the issue will most likely
     be resolved within the next several decades, say 40 to 100 years.
163. Suppose the system survives the crisis of the next several decades. By that
     time it will have to have solved, or at least brought under control, the
     principal problems that confront it, in particular that of
     human beings; that is, making people sufficiently docile so that heir
 no longer threatens the system. That being accomplished, it does
     not appear that there would be any further obstacle to the development of
     technology, and it would presumably advance toward its logical conclusion,
     which is complete control over everything on Earth, including human beings
     and all other important organisms. The system may become a unitary,
     monolithic organization, or it may be more or less fragmented and consist
     of a number of organizations coexisting in a relatioM
nship that includes
     elements of both cooperation and competition, just as today the government,
     the corporations and other large organizations both cooperate and compete
     with one another. Human freedom mostly will have vanished, because
     individuals and small groups will be impotent vis-a-vis large organizations
     armed with supertechnology and an arsenal of advanced psychological and
     biological tools for manipulating human beings, besides instruments of
     surveillance and physiM
cal coercion. Only a small number of people will have
     any real power, and even these probably will have only very limited
     freedom, because their behavior too will be regulated; just as today our
     politicians and corporation executives can retain their positions of power
     only as long as their behavior remains within certain fairly narrow limits.
t imagine that the systems will stop developing further techniques for
     controlling human beings and nature once the crisis of the neM
     is over and increasing control is no longer necessary for the system
     survival. On the contrary, once the hard times are over the system will
     increase its control over people and nature more rapidly, because it will
     no longer be hampered by difficulties of the kind that it is currently
     experiencing. Survival is not the principal motive for extending control.
     As we explained in paragraphs 87-90, technicians and scientists carry on
     their work largely as a suM
rrogate activity; that is, they satisfy their
     need for power by solving technical problems. They will continue to do this
     with unabated enthusiasm, and among the most interesting and challenging
     problems for them to solve will be those of understanding the human body
     and mind and intervening in their development. For the
165. But suppose on the other hand that the stresses of the coming decades prove
     to be too much for the system. If the systemM
 breaks down there may be
     a period of chaos, a
 such as those that history has
     recorded at various epochs in the past. It is impossible to predict what
     would emerge from such a time of troubles, but at any rate the human race
     would be given a new chance. The greatest danger is that industrial society
     may begin to reconstitute itself within the first few years after the
     breakdown. Certainly there will be many people (power-hungry types
     especially) who wM
ill be anxious to get the factories running again.
166. Therefore two tasks confront those who hate the servitude to which the
     industrial system is reducing the human race. First, we must work to
     heighten the social stresses within the system so as to increase the
     likelihood that it will break down or be weakened sufficiently so that
     a revolution against it becomes possible. Second, it is necessary to
     develop and propagate an ideology that opposes technology and the
ociety if and when the system becomes sufficiently weakened.
     And such an ideology will help to assure that, if and when industrial
     society breaks down, its remnants will be smashed beyond repair, so that
     the system cannot be reconstituted. The factories should be destroyed,
     technical books burned, etc.
167. The industrial system will not break down purely as a result of
     revolutionary action. It will not be vulnerable to revolutionary attack
     unless its own internM
al problems of development lead it into very serious
     difficulties. So if the system breaks down it will do so either
     spontaneously, or through a process that is in part spontaneous but helped
     along by revolutionaries. If the breakdown is sudden, many people will die,
     since the world
s population has become so overblown that it cannot even
     feed itself any longer without advanced technology. Even if the breakdown
     is gradual enough so that reduction of the population can occur morM
     through lowering of the birth rate than through elevation of the death
     rate, the process of de- industrialization probably will be very chaotic
     and involve much suffering. It is naive to think it likely that technology
     can be phased out in a smoothly managed, orderly way, especially since the
     technophiles will fight stubbornly at every step. Is it therefore cruel to
     work for the breakdown of the system? Maybe, but maybe not. In the first
     place, revolutionaries will not beM
 able to break the system down unless it
     is already in enough trouble so that there would be a good chance of its
     eventually breaking down by itself anyway; and the bigger the system grows,
     the more disastrous the consequences of its breakdown will be; so it may be
     that revolutionaries, by hastening the onset of the breakdown, will be
     reducing the extent of the disaster.
168. In the second place, one has to balance struggle and death against the loss
     of freedom and dignity. To mM
any of us, freedom and dignity are more
     important than a long life or avoidance of physical pain. Besides, we all
     have to die some time, and it may be better to die fighting for survival,
     or for a cause, than to live a long but empty and purposeless life.
169. In the third place, it is not at all certain that survival of the system
     will lead to less suffering than breakdown of the system would. The system
     has already caused, and is continuing to cause, immense suffering all over
 the world. Ancient cultures, that for hundreds of years gave people
     a satisfactory relationship with each other and with their environment,
     have been shattered by contact with industrial society, and the result has
     been a whole catalogue of economic, environmental, social and psychological
     problems. One of the effects of the intrusion of industrial society has
     been that over much of the world traditional controls on population have
     been thrown out of balance. Hence the populatioM
n explosion, with all that
     that implies. Then there is the psychological suffering that is widespread
     throughout the supposedly fortunate countries of the West (see paragraphs
     44, 45). No one knows what will happen as a result of ozone depletion, the
     greenhouse effect and other environmental problems that cannot yet be
     foreseen. And, as nuclear proliferation has shown, new technology cannot be
     kept out of the hands of dictators and irresponsible Third World nations.
ou like to speculate about what Iraq or North Korea will do with
     genetic engineering?
 say the technophiles,
Science is going to fix all that! We will
     conquer famine, eliminate psychological suffering, make everybody healthy
s what they said 200 years ago. The Industrial
     Revolution was supposed to eliminate poverty, make everybody happy, etc.
     The actual result has been quite different. The technophiles are hopelessly
lf-deceiving) in their understanding of social problems. They
     are unaware of (or choose to ignore) the fact that when large changes, even
     seemingly beneficial ones, are introduced into a society, they lead to
     a long sequence of other changes, most of which are impossible to predict
     (paragraph 103). The result is disruption of the society. So it is very
     probable that in their attempts to end poverty and disease, engineer
     docile, happy personalities and so forth, the technophiles wM
     social systems that are terribly troubled, even more so than the present
     once. For example, the scientists boast that they will end famine by
     creating new, genetically engineered food plants. But this will allow the
     human population to keep expanding indefinitely, and it is well known that
     crowding leads to increased stress and aggression. This is merely one
     example of the PREDICTABLE problems that will arise. We emphasize that, as
     past experience has shown, techM
nical progress will lead to other new
     problems that CANNOT be predicted in advance (paragraph 103). In fact, ever
     since the Industrial Revolution, technology has been creating new problems
     for society far more rapidly than it has been solving old ones. Thus it
     will take a long and difficult period of trial and error for the
     technophiles to work the bugs out of their Brave New World (if they every
     do). In the meantime there will be great suffering. So it is not at all
that the survival of industrial society would involve less suffering
     than the breakdown of that society would. Technology has gotten the human
     race into a fix from which there is not likely to be any easy escape.
171. But suppose now that industrial society does survive the next several
     decades and that the bugs do eventually get worked out of the system, so
     that it functions smoothly. What kind of system will it be? We will
     consider several possibilities.
us postulate that the computer scientists succeed in developing
     intelligent machines that can do all things better than human beings can do
     them. In that case presumably all work will be done by vast, highly
     organized systems of machines and no human effort will be necessary. Either
     of two cases might occur. The machines might be permitted to make all of
     their own decisions without human oversight, or else human control over the
     machines might be retained.
173. If the machines aM
re permitted to make all their own decisions, we can
     make any conjectures as to the results, because it is impossible to guess
     how such machines might behave. We only point out that the fate of the
     human race would be at the mercy of the machines. It might be argued that
     the human race would never be foolish enough to hand over all power to the
     machines. But we are suggesting neither that the human race would
     voluntarily turn power over to the machines nor that the machines wM
     willfully seize power. What we do suggest is that the human race might
     easily permit itself to drift into a position of such dependence on the
     machines that it would have no practical choice but to accept all of the
 decisions. As society and the problems that face it become more
     and more complex and as machines become more and more intelligent, people
     will let machines make more and more of their decisions for them, simply
     because machine-made decisions wilM
l bring better results than man-made
     ones. Eventually a stage may be reached at which the decisions necessary to
     keep the system running will be so complex that human beings will be
     incapable of making them intelligently. At that stage the machines will be
     in effective control. People won
t be able to just turn the machines off,
     because they will be so dependent on them that turning them off would
     amount to suicide.
174. On the other hand it is possible that human control overM
     be retained. In that case the average man may have control over certain
     private machines of his own, such as his car or his personal computer, but
     control over large systems of machines will be in the hands of a tiny
just as it is today, but with two differences. Due to improved
     techniques the elite will have greater control over the masses; and because
     human work will no longer be necessary the masses will be superfluous,
     a useless burden on the sM
ystem. If the elite is ruthless they may simply
     decide to exterminate the mass of humanity. If they are humane they may use
     propaganda or other psychological or biological techniques to reduce the
     birth rate until the mass of humanity becomes extinct, leaving the world to
     the elite. Or, if the elite consists of soft- hearted liberals, they may
     decide to play the role of good shepherds to the rest of the human race.
     They will see to it that everyone
s physical needs are satisfieM
     children are raised under psychologically hygienic conditions, that
     everyone has a wholesome hobby to keep him busy, and that anyone who may
     become dissatisfied undergoes
     life will be so purposeless that people will have to be biologically or
     psychologically engineered either to remove their need for the power
     process or to make them
 their drive for power into some
     harmless hobby. These engineM
ered human beings may be happy in such
     a society, but they most certainly will not be free. They will have been
     reduced to the status of domestic animals.
175. But suppose now that the computer scientists do not succeed in developing
     artificial intelligence, so that human work remains necessary. Even so,
     machines will take care of more and more of the simpler tasks so that there
     will be an increasing surplus of human workers at the lower levels of
     ability. (We see this happeningM
 already. There are many people who find it
     difficult or impossible to get work, because for intellectual or
     psychological reasons they cannot acquire the level of training necessary
     to make themselves useful in the present system.) On those who are
     employed, ever-increasing demands will be placed: They will need more and
     more training, more and more ability, and will have to be ever more
     reliable, conforming and docile, because they will be more and more like
ant organism. Their tasks will be increasingly specialized, so
     that their work will be, in a sense, out of touch with the real world,
     being concentrated on one tiny slice of reality. The system will have to
     use any means that it can, whether psychological or biological, to engineer
     people to be docile, to have the abilities that the system requires and to
 their drive for power into some specialized task. But the
     statement that the people of such a society will havM
     require qualification. The society may find competitiveness useful,
     provided that ways are found of directing competitiveness into channels
     that serve the needs of the system. We can imagine a future society in
     which there is endless competition for positions of prestige and power. But
     no more than a very few people will ever reach the top, where the only real
     power is (see end of paragraph 163). Very repellent is a society in which
     a person can satisfy hM
is need for power only by pushing large numbers of
     other people out of the way and depriving them of THEIR opportunity for
176. One can envision scenarios that incorporate aspects of more than one of the
     possibilities that we have just discussed. For instance, it may be that
     machines will take over most of the work that is of real, practical
     importance, but that human beings will be kept busy by being given
     relatively unimportant work. It has been suggested, for example, M
     a great development of the service industries might provide work for human
     beings. Thus people would spent their time shining each other
     driving each other around in taxicabs, making handicrafts for one another,
     waiting on each other
s tables, etc. This seems to us a thoroughly
     contemptible way for the human race to end up, and we doubt that many
     people would find fulfilling lives in such pointless busy-work. They would
     seek other, dangerous outlets (drugsM
 hate groups) unless
     they were biologically or psychologically engineered to adapt them to such
177. Needless to say, the scenarios outlined above do not exhaust all the
     possibilities. They only indicate the kinds of outcomes that seem to us
     most likely. But we can envision no plausible scenarios that are any more
     palatable than the ones we
ve just described. It is overwhelmingly probable
     that if the industrial- technological system survivesM
 the next 40 to 100
     years, it will by that time have developed certain general characteristics:
     Individuals (at least those of the
 type, who are integrated
     into the system and make it run, and who therefore have all the power) will
     be more dependent than ever on large organizations; they will be more
 than ever and their physical and mental qualities to
     a significant extent (possibly to a very great extent) will be those that
     are engineered iM
nto them rather than being the results of chance (or of
s will, or whatever); and whatever may be left of wild nature will be
     reduced to remnants preserved for scientific study and kept under the
     supervision and management of scientists (hence it will no longer be truly
     wild). In the long run (say a few centuries from now) it is likely that
     neither the human race nor any other important organisms will exist as we
     know them today, because once you start modifying organisms tM
     engineering there is no reason to stop at any particular point, so that the
     modifications will probably continue until man and other organisms have
     been utterly transformed.
178. Whatever else may be the case, it is certain that technology is creating
     for human beings a new physical and social environment radically different
     from the spectrum of environments to which natural selection has adapted
     the human race physically and psychologically. If man is not adjusteM
     this new environment by being artificially re-engineered, then he will be
     adapted to it through a long and painful process of natural selection. The
     former is far more likely than the latter.
179. It would be better to dump the whole stinking system and take the
180. The technophiles are taking us all on an utterly reckless ride into the
     unknown. Many people understand something of what technological progress is
     doing to us yet take a passive attituM
de toward it because they think it is
     inevitable. But we (FC) don
t think it is inevitable. We think it can be
     stopped, and we will give here some indications of how to go about stopping
181. As we stated in paragraph 166, the two main tasks for the present are to
     promote social stress and instability in industrial society and to develop
     and propagate an ideology that opposes technology and the industrial
     system. When the system becomes sufficiently stressed and unstable, M
     a revolution against technology may be possible. The pattern would be
     similar to that of the French and Russian Revolutions. French society and
     Russian society, for several decades prior to their respective revolutions,
     showed increasing signs of stress and weakness. Meanwhile, ideologies were
     being developed that offered a new world view that was quite different from
     the old one. In the Russian case, revolutionaries were actively working to
     undermine the old order. Then, wM
hen the old system was put under sufficient
     additional stress (by financial crisis in France, by military defeat in
     Russia) it was swept away by revolution. What we propose is something along
     the same lines.
182. It will be objected that the French and Russian Revolutions were failures.
     But most revolutions have two goals. One is to destroy an old form of
     society and the other is to set up the new form of society envisioned by
     the revolutionaries. The French and Russian revolutiM
     (fortunately!) to create the new kind of society of which they dreamed, but
     they were quite successful in destroying the old society. We have no
     illusions about the feasibility of creating a new, ideal form of society.
     Our goal is only to destroy the existing form of society.
183. But an ideology, in order to gain enthusiastic support, must have
     a positive ideal as well as a negative one; it must be FOR something as
     well as AGAINST something. The positive ideal thM
at we propose is Nature.
     That is, WILD nature: those aspects of the functioning of the Earth and its
     living things that are independent of human management and free of human
     interference and control. And with wild nature we include human nature, by
     which we mean those aspects of the functioning of the human individual that
     are not subject to regulation by organized society but are products of
     chance, or free will, or God (depending on your religious or philosophical
184. Nature makes a perfect counter-ideal to technology for several reasons.
     Nature (that which is outside the power of the system) is the opposite of
     technology (which seeks to expand indefinitely the power of the system).
     Most people will agree that nature is beautiful; certainly it has
     tremendous popular appeal. The radical environmentalists ALREADY hold an
     ideology that exalts nature and opposes technology. [30] It is not
     necessary for the sake of nature to set up some cM
himerical utopia or any
     new kind of social order. Nature takes care of itself: It was a spontaneous
     creation that existed long before any human society, and for countless
     centuries many different kinds of human societies coexisted with nature
     without doing it an excessive amount of damage. Only with the Industrial
     Revolution did the effect of human society on nature become really
     devastating. To relieve the pressure on nature it is not necessary to
     create a special kind of M
social system, it is only necessary to get rid of
     industrial society. Granted, this will not solve all problems. Industrial
     society has already done tremendous damage to nature and it will take
     a very long time for the scars to heal. Besides, even pre-industrial
     societies can do significant damage to nature. Nevertheless, getting rid of
     industrial society will accomplish a great deal. It will relieve the worst
     of the pressure on nature so that the scars can begin to heal. It willM
     remove the capacity of organized society to keep increasing its control
     over nature (including human nature). Whatever kind of society may exist
     after the demise of the industrial system, it is certain that most people
     will live close to nature, because in the absence of advanced technology
     there is no other way that people CAN live. To feed themselves they must be
     peasants or herdsmen or fishermen or hunters, etc. And, generally speaking,
     local autonomy should tend to incM
rease, because lack of advanced technology
     and rapid communications will limit the capacity of governments or other
     large organizations to control local communities.
185. As for the negative consequences of eliminating industrial society
t eat your cake and have it too. To gain one thing you have to
     sacrifice another.
186. Most people hate psychological conflict. For this reason they avoid doing
     any serious thinking about difficult social issues, and they like to haM
     such issues presented to them in simple, black-and-white terms: THIS is all
     good and THAT is all bad. The revolutionary ideology should therefore be
     developed on two levels.
187. On the more sophisticated level the ideology should address itself to
     people who are intelligent, thoughtful and rational. The object should be
     to create a core of people who will be opposed to the industrial system on
     a rational, thought-out basis, with full appreciation of the problems and
biguities involved, and of the price that has to be paid for getting rid
     of the system. It is particularly important to attract people of this type,
     as they are capable people and will be instrumental in influencing others.
     These people should be addressed on as rational a level as possible. Facts
     should never intentionally be distorted and intemperate language should be
     avoided. This does not mean that no appeal can be made to the emotions, but
     in making such appeal care should M
be taken to avoid misrepresenting the
     truth or doing anything else that would destroy the intellectual
     respectability of the ideology.
188. On a second level, the ideology should be propagated in a simplified form
     that will enable the unthinking majority to see the conflict of technology
     vs. nature in unambiguous terms. But even on this second level the ideology
     should not be expressed in language that is so cheap, intemperate or
     irrational that it alienates people of the thoughM
tful and rational type.
     Cheap, intemperate propaganda sometimes achieves impressive short-term
     gains, but it will be more advantageous in the long run to keep the loyalty
     of a small number of intelligently committed people than to arouse the
     passions of an unthinking, fickle mob who will change their attitude as
     soon as someone comes along with a better propaganda gimmick. However,
     propaganda of the rabble-rousing type may be necessary when the system is
     nearing the point oM
f collapse and there is a final struggle between rival
     ideologies to determine which will become dominant when the old world-view
189. Prior to that final struggle, the revolutionaries should not expect to have
     a majority of people on their side. History is made by active, determined
     minorities, not by the majority, which seldom has a clear and consistent
     idea of what it really wants. Until the time comes for the final push
     toward revolution [31], the task of revolutM
ionaries will be less to win the
     shallow support of the majority than to build a small core of deeply
     committed people. As for the majority, it will be enough to make them aware
     of the existence of the new ideology and remind them of it frequently;
     though of course it will be desirable to get majority support to the extent
     that this can be done without weakening the core of seriously committed
190. Any kind of social conflict helps to destabilize the system, but one shouM
     be careful about what kind of conflict one encourages. The line of conflict
     should be drawn between the mass of the people and the power-holding elite
     of industrial society (politicians, scientists, upper-level business
     executives, government officials, etc.). It should NOT be drawn between the
     revolutionaries and the mass of the people. For example, it would be bad
     strategy for the revolutionaries to condemn Americans for their habits of
     consumption. Instead, the averagM
e American should be portrayed as a victim
     of the advertising and marketing industry, which has suckered him into
     buying a lot of junk that he doesn
t need and that is very poor
     compensation for his lost freedom. Either approach is consistent with the
     facts. It is merely a matter of attitude whether you blame the advertising
     industry for manipulating the public or blame the public for allowing
     itself to be manipulated. As a matter of strategy one should generally
191. One should think twice before encouraging any other social conflict than
     that between the power- holding elite (which wields technology) and the
     general public (over which technology exerts its power). For one thing,
     other conflicts tend to distract attention from the important conflicts
     (between power-elite and ordinary people, between technology and nature);
     for another thing, other conflicts may actually tend to encourage
     technologization, because each M
side in such a conflict wants to use
     technological power to gain advantages over its adversary. This is clearly
     seen in rivalries between nations. It also appears in ethnic conflicts
     within nations. For example, in America many black leaders are anxious to
     gain power for African Americans by placing back individuals in the
     technological power-elite. They want there to be many black government
     officials, scientists, corporation executives and so forth. In this way
elping to absorb the African American subculture into the
     technological system. Generally speaking, one should encourage only those
     social conflicts that can be fitted into the framework of the conflicts of
     power-elite vs. ordinary people, technology vs nature.
192. But the way to discourage ethnic conflict is NOT through militant advocacy
     of minority rights (see paragraphs 21, 29). Instead, the revolutionaries
     should emphasize that although minorities do suffer more or less
advantage, this disadvantage is of peripheral significance. Our real
     enemy is the industrial- technological system, and in the struggle against
     the system, ethnic distinctions are of no importance.
193. The kind of revolution we have in mind will not necessarily involve an
     armed uprising against any government. It may or may not involve physical
     violence, but it will not be a POLITICAL revolution. Its focus will be on
     technology and economics, not politics. [32]
194. Probably the revM
olutionaries should even AVOID assuming political power,
     whether by legal or illegal means, until the industrial system is stressed
     to the danger point and has proved itself to be a failure in the eyes of
     most people. Suppose for example that some
 party should win control
     of the United States Congress in an election. In order to avoid betraying
     or watering down their own ideology they would have to take vigorous
     measures to turn economic growth into economic shrinkageM
     man the results would appear disastrous: There would be massive
     unemployment, shortages of commodities, etc. Even if the grosser ill
     effects could be avoided through superhumanly skillful management, still
     people would have to begin giving up the luxuries to which they have become
     addicted. Dissatisfaction would grow, the
 party would be voted out
     of office and the revolutionaries would have suffered a severe setback. For
     this reason the revolutiM
onaries should not try to acquire political power
     until the system has gotten itself into such a mess that any hardships will
     be seen as resulting from the failures of the industrial system itself and
     not from the policies of the revolutionaries. The revolution against
     technology will probably have to be a revolution by outsiders, a revolution
     from below and not from above.
195. The revolution must be international and worldwide. It cannot be carried
     out on a nation-by-nation baM
sis. Whenever it is suggested that the United
     States, for example, should cut back on technological progress or economic
     growth, people get hysterical and start screaming that if we fall behind in
     technology the Japanese will get ahead of us. Holy robots! The world will
     fly off its orbit if the Japanese ever sell more cars than we do!
     (Nationalism is a great promoter of technology.) More reasonably, it is
     argued that if the relatively democratic nations of the world fall behind
     in technology while nasty, dictatorial nations like China, Vietnam and
     North Korea continue to progress, eventually the dictators may come to
     dominate the world. That is why the industrial system should be attacked in
     all nations simultaneously, to the extent that this may be possible. True,
     there is no assurance that the industrial system can be destroyed at
     approximately the same time all over the world, and it is even conceivable
     that the attempt to overthrow the system cM
ould lead instead to the
     domination of the system by dictators. That is a risk that has to be taken.
     And it is worth taking, since the difference between a
     industrial system and one controlled by dictators is small compared with
     the difference between an industrial system and a non-industrial one. [33]
     It might even be argued that an industrial system controlled by dictators
     would be preferable, because dictator-controlled systems usually have
cient, hence they are presumably more likely to break down.
196. Revolutionaries might consider favoring measures that tend to bind the
     world economy into a unified whole. Free trade agreements like NAFTA and
     GATT are probably harmful to the environment in the short run, but in the
     long run they may perhaps be advantageous because they foster economic
     interdependence between nations. It will be easier to destroy the
     industrial system on a worldwide basis if the worM
ld economy is so unified
     that its breakdown in any one major nation will lead to its breakdown in
     all industrialized nations.
197. Some people take the line that modern man has too much power, too much
     control over nature; they argue for a more passive attitude on the part of
     the human race. At best these people are expressing themselves unclearly,
     because they fail to distinguish between power for LARGE ORGANIZATIONS and
     power for INDIVIDUALS and SMALL GROUPS. It is a mistake tM
     powerlessness and passivity, because people NEED power. Modern man as
     a collective entity
that is, the industrial system
has immense power over
     nature, and we (FC) regard this as evil. But modern INDIVIDUALS and SMALL
     GROUPS OF INDIVIDUALS have far less power than primitive man ever did.
     Generally speaking, the vast power of
 over nature is exercised
     not by individuals or small groups but by large organizations. To the
     extent that the averageM
 modern INDIVIDUAL can wield the power of
     technology, he is permitted to do so only within narrow limits and only
     under the supervision and control of the system. (You need a license for
     everything and with the license come rules and regulations.) The individual
     has only those technological powers with which the system chooses to
     provide him. His PERSONAL power over nature is slight.
198. Primitive INDIVIDUALS and SMALL GROUPS actually had considerable power over
     nature; or maybM
e it would be better to say power WITHIN nature. When
     primitive man needed food he knew how to find and prepare edible roots, how
     to track game and take it with homemade weapons. He knew how to protect
     himself from heat, cold, rain, dangerous animals, etc. But primitive man
     did relatively little damage to nature because the COLLECTIVE power of
     primitive society was negligible compared to the COLLECTIVE power of
     industrial society.
199. Instead of arguing for powerlessness and paM
ssivity, one should argue that
     the power of the INDUSTRIAL SYSTEM should be broken, and that this will
     greatly INCREASE the power and freedom of INDIVIDUALS and SMALL GROUPS.
200. Until the industrial system has been thoroughly wrecked, the destruction of
     that system must be the revolutionaries
 ONLY goal. Other goals would
     distract attention and energy from the main goal. More importantly, if the
     revolutionaries permit themselves to have any other goal than the
     destruction ofM
 technology, they will be tempted to use technology as a tool
     for reaching that other goal. If they give in to that temptation, they will
     fall right back into the technological trap, because modern technology is
     a unified, tightly organized system, so that, in order to retain SOME
     technology, one finds oneself obliged to retain MOST technology, hence one
     ends up sacrificing only token amounts of technology.
201. Suppose for example that the revolutionaries took
     a goal. Human nature being what it is, social justice would not come about
     spontaneously; it would have to be enforced. In order to enforce it the
     revolutionaries would have to retain central organization and control. For
     that they would need rapid long-distance transportation and communication,
     and therefore all the technology needed to support the transportation and
     communication systems. To feed and clothe poor people they would have to
     use agricultural and manufacturinM
g technology. And so forth. So that the
     attempt to insure social justice would force them to retain most parts of
     the technological system. Not that we have anything against social justice,
     but it must not be allowed to interfere with the effort to get rid of the
     technological system.
202. It would be hopeless for revolutionaries to try to attack the system
     without using SOME modern technology. If nothing else they must use the
     communications media to spread their message. But tM
hey should use modern
     technology for only ONE purpose: to attack the technological system.
203. Imagine an alcoholic sitting with a barrel of wine in front of him. Suppose
     he starts saying to himself,
t bad for you if used in moderation.
     Why, they say small amounts of wine are even good for you! It won
     any harm if I take just one little drink....
 Well you know what is going
     to happen. Never forget that the human race with technology is just like an
coholic with a barrel of wine.
204. Revolutionaries should have as many children as they can. There is strong
     scientific evidence that social attitudes are to a significant extent
     inherited. No one suggests that a social attitude is a direct outcome of
s genetic constitution, but it appears that personality traits are
     partly inherited and that certain personality traits tend, within the
     context of our society, to make a person more likely to hold this or that
ttitude. Objections to these findings have been raised, but the
     objections are feeble and seem to be ideologically motivated. In any event,
     no one denies that children tend on the average to hold social attitudes
     similar to those of their parents. From our point of view it doesn
     all that much whether the attitudes are passed on genetically or through
     childhood training. In either case they ARE passed on.
205. The trouble is that many of the people who are inclined to rebel M
     the industrial system are also concerned about the population problems,
     hence they are apt to have few or no children. In this way they may be
     handing the world over to the sort of people who support or at least accept
     the industrial system. To insure the strength of the next generation of
     revolutionaries the present generation should reproduce itself abundantly.
     In doing so they will be worsening the population problem only slightly.
     And the important problem is toM
 get rid of the industrial system, because
     once the industrial system is gone the world
s population necessarily will
     decrease (see paragraph 167); whereas, if the industrial system survives,
     it will continue developing new techniques of food production that may
     enable the world
s population to keep increasing almost indefinitely.
206. With regard to revolutionary strategy, the only points on which we
     absolutely insist are that the single overriding goal must be the
ion of modern technology, and that no other goal can be allowed to
     compete with this one. For the rest, revolutionaries should take an
     empirical approach. If experience indicates that some of the
     recommendations made in the foregoing paragraphs are not going to give good
     results, then those recommendations should be discarded.
TWO KINDS OF TECHNOLOGY
207. An argument likely to be raised against our proposed revolution is that it
     is bound to fail, because (it is claimed) throughout hiM
story technology has
     always progressed, never regressed, hence technological regression is
     impossible. But this claim is false.
208. We distinguish between two kinds of technology, which we will call
     small-scale technology and organization-dependent technology. Small-scale
     technology is technology that can be used by small-scale communities
     without outside assistance. Organization-dependent technology is technology
     that depends on large-scale social organization. We are aware ofM
     significant cases of regression in small-scale technology. But
     organization-dependent technology DOES regress when the social organization
     on which it depends breaks down. Example: When the Roman Empire fell apart
 small-scale technology survived because any clever village
     craftsman could build, for instance, a water wheel, any skilled smith could
     make steel by Roman methods, and so forth. But the Romans
     organization-dependent technology DID regress. TheM
ir aqueducts fell into
     disrepair and were never rebuilt. Their techniques of road construction
     were lost. The Roman system of urban sanitation was forgotten, so that not
     until rather recent times did the sanitation of European cities equal that
     of Ancient Rome.
209. The reason why technology has seemed always to progress is that, until
     perhaps a century or two before the Industrial Revolution, most technology
     was small-scale technology. But most of the technology developed sinceM
     Industrial Revolution is organization-dependent technology. Take the
     refrigerator for example. Without factory-made parts or the facilities of
     a post-industrial machine shop it would be virtually impossible for
     a handful of local craftsmen to build a refrigerator. If by some miracle
     they did succeed in building one it would be useless to them without
     a reliable source of electric power. So they would have to dam a stream and
     build a generator. Generators require large M
amounts of copper wire. Imagine
     trying to make that wire without modern machinery. And where would they get
     a gas suitable for refrigeration? It would be much easier to build an
     icehouse or preserve food by drying or picking, as was done before the
     invention of the refrigerator.
210. So it is clear that if the industrial system were once thoroughly broken
     down, refrigeration technology would quickly be lost. The same is true of
     other organization-dependent technology. And once tM
his technology had been
     lost for a generation or so it would take centuries to rebuild it, just as
     it took centuries to build it the first time around. Surviving technical
     books would be few and scattered. An industrial society, if built from
     scratch without outside help, can only be built in a series of stages: You
     need tools to make tools to make tools to make tools ... . A long process
     of economic development and progress in social organization is required.
the absence of an ideology opposed to technology, there is no
     reason to believe that anyone would be interested in rebuilding industrial
     society. The enthusiasm for
 is a phenomenon peculiar to the
     modern form of society, and it seems not to have existed prior to the 17th
     century or thereabouts.
211. In the late Middle Ages there were four main civilizations that were about
: Europe, the Islamic world, India, and the Far East
     (China, Japan, KoM
rea). Three of those civilizations remained more or less
     stable, and only Europe became dynamic. No one knows why Europe became
     dynamic at that time; historians have their theories but these are only
     speculation. At any rate, it is clear that rapid development toward
     a technological form of society occurs only under special conditions. So
     there is no reason to assume that a long-lasting technological regression
     cannot be brought about.
212. Would society EVENTUALLY develop againM
 toward an industrial-technological
     form? Maybe, but there is no use in worrying about it, since we can
     predict or control events 500 or 1,000 years in the future. Those problems
     must be dealt with by the people who will live at that time.
THE DANGER OF LEFTISM
213. Because of their need for rebellion and for membership in a movement,
     leftists or persons of similar psychological type often are unattracted to
     a rebellious or activist movement whose goals and membership are not
   initially leftist. The resulting influx of leftish types can easily turn
     a non-leftist movement into a leftist one, so that leftist goals replace or
     distort the original goals of the movement.
214. To avoid this, a movement that exalts nature and opposes technology must
     take a resolutely anti-leftist stance and must avoid all collaboration with
     leftists. Leftism is in the long run inconsistent with wild nature, with
     human freedom and with the elimination of modern technology. LeftiM
     collectivist; it seeks to bind together the entire world (both nature and
     the human race) into a unified whole. But this implies management of nature
     and of human life by organized society, and it requires advanced
     technology. You can
t have a united world without rapid transportation and
     communication, you can
t make all people love one another without
     sophisticated psychological techniques, you can
     without the necessary technologM
ical base. Above all, leftism is driven by
     the need for power, and the leftist seeks power on a collective basis,
     through identification with a mass movement or an organization. Leftism is
     unlikely ever to give up technology, because technology is too valuable
     a source of collective power.
215. The anarchist [34] too seeks power, but he seeks it on an individual or
     small-group basis; he wants individuals and small groups to be able to
     control the circumstances of their own livesM
. He opposes technology because
     it makes small groups dependent on large organizations.
216. Some leftists may seem to oppose technology, but they will oppose it only
     so long as they are outsiders and the technological system is controlled by
     non-leftists. If leftism ever becomes dominant in society, so that the
     technological system becomes a tool in the hands of leftists, they will
     enthusiastically use it and promote its growth. In doing this they will be
     repeating a pattern thM
at leftism has shown again and again in the past.
     When the Bolsheviks in Russia were outsiders, they vigorously opposed
     censorship and the secret police, they advocated self-determination for
     ethnic minorities, and so forth; but as soon as they came into power
     themselves, they imposed a tighter censorship and created a more ruthless
     secret police than any that had existed under the tsars, and they oppressed
     ethnic minorities at least as much as the tsars had done. In the United
     States, a couple of decades ago when leftists were a minority in our
     universities, leftist professors were vigorous proponents of academic
     freedom, but today, in those of our universities where leftists have become
     dominant, they have shown themselves ready to take away from everyone
s academic freedom. (This is
political correctness.
     happen with leftists and technology: They will use it to oppress everyone
     else if they ever get it under their ownM
217. In earlier revolutions, leftists of the most power-hungry type, repeatedly,
     have first cooperated with non-leftist revolutionaries, as well as with
     leftists of a more libertarian inclination, and later have double- crossed
     them to seize power for themselves. Robespierre did this in the French
     Revolution, the Bolsheviks did it in the Russian Revolution, the communists
     did it in Spain in 1938 and Castro and his followers did it in Cuba. Given
     the past history of lefM
tism, it would be utterly foolish for non-leftist
     revolutionaries today to collaborate with leftists.
218. Various thinkers have pointed out that leftism is a kind of religion.
     Leftism is not a religion in the strict sense because leftist doctrine does
     not postulate the existence of any supernatural being. But, for the
     leftist, leftism plays a psychological role much like that which religion
     plays for some people. The leftist NEEDS to believe in leftism; it plays
     a vital role inM
 his psychological economy. His beliefs are not easily
     modified by logic or facts. He has a deep conviction that leftism is
     morally Right with a capital R, and that he has not only a right but a duty
     to impose leftist morality on everyone. (However, many of the people we are
     referring to as
 do not think of themselves as leftists and would
     not describe their system of beliefs as leftism. We use the term
t know of any better words to deM
signate the spectrum of
     related creeds that includes the feminist, gay rights, political
     correctness, etc., movements, and because these movements have a strong
     affinity with the old left. See paragraphs 227-230.)
219. Leftism is a totalitarian force. Wherever leftism is in a position of power
     it tends to invade every private corner and force every thought into
     a leftist mold. In part this is because of the quasi-religious character of
     leftism; everything contrary to leftist belM
iefs represents Sin. More
     importantly, leftism is a totalitarian force because of the leftists
     for power. The leftist seeks to satisfy his need for power through
     identification with a social movement and he tries to go through the power
     process by helping to pursue and attain the goals of the movement (see
     paragraph 83). But no matter how far the movement has gone in attaining its
     goals the leftist is never satisfied, because his activism is a surrogate
ee paragraph 41). That is, the leftist
s real motive is not to
     attain the ostensible goals of leftism; in reality he is motivated by the
     sense of power he gets from struggling for and then reaching a social goal.
     [35] Consequently the leftist is never satisfied with the goals he has
     already attained; his need for the power process leads him always to pursue
     some new goal. The leftist wants equal opportunities for minorities. When
     that is attained he insists on statistical equalM
ity of achievement by
     minorities. And as long as anyone harbors in some corner of his mind
     a negative attitude toward some minority, the leftist has to re-educated
     him. And ethnic minorities are not enough; no one can be allowed to have
     a negative attitude toward homosexuals, disabled people, fat people, old
     people, ugly people, and on and on and on. It
s not enough that the public
     should be informed about the hazards of smoking; a warning has to be
     stamped on every packaM
ge of cigarettes. Then cigarette advertising has to
     be restricted if not banned. The activists will never be satisfied until
     tobacco is outlawed, and after that it will be alcohol, then junk food,
     etc. Activists have fought gross child abuse, which is reasonable. But now
     they want to stop all spanking. When they have done that they will want to
     ban something else they consider unwholesome, then another thing and then
     another. They will never be satisfied until they have complete M
     all child rearing practices. And then they will move on to another cause.
220. Suppose you asked leftists to make a list of ALL the things that were wrong
     with society, and then suppose you instituted EVERY social change that they
     demanded. It is safe to say that within a couple of years the majority of
     leftists would find something new to complain about, some new social
     to correct because, once again, the leftist is motivated less by distress
s ills than by the need to satisfy his drive for power by
     imposing his solutions on society.
221. Because of the restrictions placed on their thoughts and behavior by their
     high level of socialization, many leftists of the over-socialized type
     cannot pursue power in the ways that other people do. For them the drive
     for power has only one morally acceptable outlet, and that is in the
     struggle to impose their morality on everyone.
222. Leftists, especially those of the oversocializedM
 type, are True Believers
     in the sense of Eric Hoffer
     Believers are of the same psychological type as leftists. Presumably
     a true-believing nazi, for instance, is very different psychologically from
     a true-believing leftist. Because of their capacity for single-minded
     devotion to a cause, True Believers are a useful, perhaps a necessary,
     ingredient of any revolutionary movement. This presents a problem with
     which we must aM
t know how to deal. We aren
     harness the energies of the True Believer to a revolution against
     technology. At present all we can say is that no True Believer will make
     a safe recruit to the revolution unless his commitment is exclusively to
     the destruction of technology. If he is committed also to another ideal, he
     may want to use technology as a tool for pursuing that other ideal (see
     paragraphs 220, 221).
223. Some readers may say,
out leftism is a lot of crap. I know
     John and Jane who are leftish types and they don
     totalitarian tendencies.
s quite true that many leftists, possibly even
     a numerical majority, are decent people who sincerely believe in tolerating
 values (up to a point) and wouldn
t want to use high-handed methods
     to reach their social goals. Our remarks about leftism are not meant to
     apply to every individual leftist but to describe the general characterM
     leftism as a movement. And the general character of a movement is not
     necessarily determined by the numerical proportions of the various kinds of
     people involved in the movement.
224. The people who rise to positions of power in leftist movements tend to be
     leftists of the most power- hungry type, because power-hungry people are
     those who strive hardest to get into positions of power. Once the
     power-hungry types have captured control of the movement, there are many
tists of a gentler breed who inwardly disapprove of many of the actions
     of the leaders, but cannot bring themselves to oppose them. They NEED their
     faith in the movement, and because they cannot give up this faith they go
     along with the leaders. True, SOME leftists do have the guts to oppose the
     totalitarian tendencies that emerge, but they generally lose, because the
     power-hungry types are better organized, are more ruthless and
     Machiavellian and have taken care to build themselM
ves a strong power base.
225. These phenomena appeared clearly in Russia and other countries that were
     taken over by leftists. Similarly, before the breakdown of communism in the
     USSR, leftish types in the West would seldom criticize that country. If
     prodded they would admit that the USSR did many wrong things, but then they
     would try to find excuses for the communists and begin talking about the
     faults of the West. They always opposed Western military resistance to
gression. Leftish types all over the world vigorously protested
     the U.S. military action in Vietnam, but when the USSR invaded Afghanistan
     they did nothing. Not that they approved of the Soviet actions; but because
     of their leftist faith, they just couldn
t bear to put themselves in
     opposition to communism. Today, in those of our universities where
political correctness
 has become dominant, there are probably many
     leftish types who privately disapprove of the suppression M
     freedom, but they go along with it anyway.
226. Thus the fact that many individual leftists are personally mild and fairly
     tolerant people by no means prevents leftism as a whole form having
     a totalitarian tendency.
227. Our discussion of leftism has a serious weakness. It is still far from
     clear what we mean by the word
t seem to be much we
     can do about this. Today leftism is fragmented into a whole spectrum of
     activist movements. Yet nM
ot all activist movements are leftist, and some
     activist movements (e.g., radical environmentalism) seem to include both
     personalities of the leftist type and personalities of thoroughly
     un-leftist types who ought to know better than to collaborate with
     leftists. Varieties of leftists fade out gradually into varieties of
     non-leftists and we ourselves would often be hard-pressed to decide whether
     a given individual is or is not a leftist. To the extent that it is defined
all, our conception of leftism is defined by the discussion of it that
     we have given in this article, and we can only advise the reader to use his
     own judgment in deciding who is a leftist.
228. But it will be helpful to list some criteria for diagnosing leftism. These
     criteria cannot be applied in a cut and dried manner. Some individuals may
     meet some of the criteria without being leftists, some leftists may not
     meet any of the criteria. Again, you just have to use your judgment.
9. The leftist is oriented toward large-scale collectivism. He emphasizes the
     duty of the individual to serve society and the duty of society to take
     care of the individual. He has a negative attitude toward individualism. He
     often takes a moralistic tone. He tends to be for gun control, for sex
     education and other psychologically
 educational methods, for
     social planning, for affirmative action, for multiculturalism. He tends to
     identify with victims. He tends tM
o be against competition and against
     violence, but he often finds excuses for those leftists who do commit
     violence. He is fond of using the common catch- phrases of the left, like
     responsibility.
 Maybe the best diagnostic trait of the leftist is his
     tendency to sympathize with the following movements: femM
     ethnic rights, disability rights, animal rights, political correctness.
     Anyone who strongly sympathizes with ALL of these movements is almost
     certainly a leftist. [36]
230. The more dangerous leftists, that is, those who are most power-hungry, are
     often characterized by arrogance or by a dogmatic approach to ideology.
     However, the most dangerous leftists of all may be certain oversocialized
     types who avoid irritating displays of aggressiveness and refrain fromM
     advertising their leftism, but work quietly and unobtrusively to promote
     collectivist values,
 psychological techniques for socializing
     children, dependence of the individual on the system, and so forth. These
     crypto- leftists (as we may call them) approximate certain bourgeois types
     as far as practical action is concerned, but differ from them in
     psychology, ideology and motivation. The ordinary bourgeois tries to bring
     people under control of the system M
in order to protect his way of life, or
     he does so simply because his attitudes are conventional. The
     crypto-leftist tries to bring people under control of the system because he
     is a True Believer in a collectivistic ideology. The crypto-leftist is
     differentiated from the average leftist of the oversocialized type by the
     fact that his rebellious impulse is weaker and he is more securely
     socialized. He is differentiated from the ordinary well-socialized
     bourgeois by the factM
 that there is some deep lack within him that makes it
     necessary for him to devote himself to a cause and immerse himself in
     a collectivity. And maybe his (well-sublimated) drive for power is stronger
     than that of the average bourgeois.
231. Throughout this article we
ve made imprecise statements and statements that
     ought to have had all sorts of qualifications and reservations attached to
     them; and some of our statements may be flatly false. Lack of sufficient
ormation and the need for brevity made it impossible for us to formulate
     our assertions more precisely or add all the necessary qualifications. And
     of course in a discussion of this kind one must rely heavily on intuitive
     judgment, and that can sometimes be wrong. So we don
     article expresses more than a crude approximation to the truth.
232. All the same, we are reasonably confident that the general outlines of the
     picture we have painted here are roughly correct. M
Just one possible weak
     point needs to be mentioned. We have portrayed leftism in its modern form
     as a phenomenon peculiar to our time and as a symptom of the disruption of
     the power process. But we might possibly be wrong about this.
     Oversocialized types who try to satisfy their drive for power by imposing
     their morality on everyone have certainly been around for a long time. But
     we THINK that the decisive role played by feelings of inferiority, low
     self-esteem, powerlessneM
ss, identification with victims by people who are
     not themselves victims, is a peculiarity of modern leftism. Identification
     with victims by people not themselves victims can be seen to some extent in
     19th century leftism and early Christianity but as far as we can make out,
     symptoms of low self-esteem, etc., were not nearly so evident in these
     movements, or in any other movements, as they are in modern leftism. But we
     are not in a position to assert confidently that no such moveM
     existed prior to modern leftism. This is a significant question to which
     historians ought to give their attention.
1. (Paragraph 19) We are asserting that ALL, or even most, bullies and ruthless
   competitors suffer from feelings of inferiority.
2. (Paragraph 25) During the Victorian period many oversocialized people
   suffered from serious psychological problems as a result of repressing or
   trying to repress their sexual feelings. Freud apparently based his theories
eople of this type. Today the focus of socialization has shifted from sex
3. (Paragraph 27) Not necessarily including specialists in engineering or the
4. (Paragraph 28) There are many individuals of the middle and upper classes who
   resist some of these values, but usually their resistance is more or less
   covert. Such resistance appears in the mass media only to a very limited
   extent. The main thrust of propaganda in our society is in favor of the
   The main reason why these values have become, so to speak, the official values
   of our society is that they are useful to the industrial system. Violence is
   discouraged because it disrupts the functioning of the system. Racism is
   discouraged because ethnic conflicts also disrupt the system, and
   discrimination wastes the talents of minority-group members who could be
   useful to the system. Poverty must be
 because the underclass causes
   problems for the system and contacM
t with the underclass lowers the morale of
   the other classes. Women are encouraged to have careers because their talents
   are useful to the system and, more importantly, because by having regular
   jobs women become better integrated into the system and tied directly to it
   rather than to their families. This helps to weaken family solidarity. (The
   leaders of the system say they want to strengthen the family, but they really
   mean is that they want the family to serve as an effective tool for
cializing children in accord with the needs of the system. We argue in
   paragraphs 51, 52 that the system cannot afford to let the family or other
   small-scale social groups be strong or autonomous.)
5. (Paragraph 42) It may be argued that the majority of people don
   make their own decisions but want leaders to do their thinking for them.
   There is an element of truth in this. People like to make their own decisions
   in small matters, but making decisions on difficult, fundamental questiM
   requires facing up to psychological conflict, and most people hate
   psychological conflict. Hence they tend to lean on others in making difficult
   decisions. But it does not follow that they like to have decisions imposed
   upon them without having any opportunity to influence those decisions. The
   majority of people are natural followers, not leaders, but they like to have
   direct personal access to their leaders, they want to be able to influence
   the leaders and participate to some extenM
t in making even the difficult
   decisions. At least to that degree they need autonomy.
6. (Paragraph 44) Some of the symptoms listed are similar to those shown by
   To explain how these symptoms arise from deprivation with respect to the
   Common-sense understanding of human nature tells one that lack of goals whose
   attainment requires effort leads to boredom and that boredom, long continued,
   often leads eventually to depression. Failure to attain goals leaM
   frustration and lowering of self-esteem. Frustration leads to anger, anger to
   aggression, often in the form of spouse or child abuse. It has been shown that
   long-continued frustration commonly leads to depression and that depression
   tends to cause guilt, sleep disorders, eating disorders and bad feelings about
   oneself. Those who are tending toward depression seek pleasure as an antidote;
   hence insatiable hedonism and excessive sex, with perversions as a means of
   getting new kicks. M
Boredom too tends to cause excessive pleasure-seeking since,
   lacking other goals, people often use pleasure as a goal. See accompanying
   The foregoing is a simplification. Reality is more complex, and of course,
   deprivation with respect to the power process is not the ONLY cause of the
   symptoms described.
   By the way, when we mention depression we do not necessarily mean depression
   that is severe enough to be treated by a psychiatrist. Often only mild forms of
 are involved. And when we speak of goals we do not necessarily mean
   long-term, thought-out goals. For many or most people through much of human
   history, the goals of a hand-to-mouth existence (merely providing oneself and
s family with food from day to day) have been quite sufficient.
7. (Paragraph 52) A partial exception may be made for a few passive,
   inward-looking groups, such as the Amish, which have little effect on the
   wider society. Apart from these, some genuine small-scale commuM
   exist in America today. For instance, youth gangs and
   regards them as dangerous, and so they are, because the members of these
   groups are loyal primarily to one another rather than to the system, hence
   the system cannot control them.
   Or take the gypsies. The gypsies commonly get away with theft and fraud because
   their loyalties are such that they can always get other gypsies to give
 their innocence. Obviously the system wouldM
   serious trouble if too many people belonged to such groups.
   Some of the early-20th century Chinese thinkers who were concerned with
   modernizing China recognized the necessity breaking down small-scale social
   groups such as the family:
(According to Sun Yat-sen) the Chinese people needed
   a new surge of patriotism, which would lead to a transfer of loyalty from the
   family to the state.... (According to Li Huang) traditional attachments,
   particularly to the family had to be abaM
ndoned if nationalism were to develop in
Chinese Political Thought in the Twentieth Century,
   page 125, page 297.)
8. (Paragraph 56) Yes, we know that 19th century America had its problems, and
   serious ones, but for the sake of brevity we have to express ourselves in
   simplified terms.
9. (Paragraph 61) We leave aside the
 We are speaking of the
10. (Paragraph 62) Some social scientists, educators,
als and the like are doing their best to push the social drives
    into group 1 by trying to see to it that everyone has a satisfactory social
11. (Paragraphs 63, 82) Is the drive for endless material acquisition really an
    artificial creation of the advertising and marketing industry? Certainly
    there is no innate human drive for material acquisition. There have been
    many cultures in which people have desired little material wealth beyond
    what was necessary to satisfy their basic phM
ysical needs (Australian
    aborigines, traditional Mexican peasant culture, some African cultures). On
    the other hand there have also been many pre-industrial cultures in which
    material acquisition has played an important role. So we can
s acquisition-oriented culture is exclusively a creation of the
    advertising and marketing industry. But it is clear that the advertising and
    marketing industry has had an important part in creating that culture. The
ations that spend millions on advertising wouldn
    that kind of money without solid proof that they were getting it back in
    increased sales. One member of FC met a sales manager a couple of years ago
    who was frank enough to tell him,
Our job is to make people buy things they
 He then described how an untrained novice could
    present people with the facts about a product, and make no sales at all,
    while a trained and experienced professionM
al salesman would make lots of
    sales to the same people. This shows that people are manipulated into buying
12. (Paragraph 64) The problem of purposelessness seems to have become less
    serious during the last 15 years or so, because people now feel less secure
    physically and economically than they did earlier, and the need for security
    provides them with a goal. But purposelessness has been replaced by
    frustration over the difficulty of attaining securiM
ty. We emphasize the
    problem of purposelessness because the liberals and leftists would wish to
    solve our social problems by having society guarantee everyone
    but if that could be done it would only bring back the problem of
    purposelessness. The real issue is not whether society provides well or
    poorly for people
s security; the trouble is that people are dependent on
    the system for their security rather than having it in their own hands.
    This, by the way, is part M
of the reason why some people get worked up about
    the right to bear arms; possession of a gun puts that aspect of their
    security in their own hands.
13. (Paragraph 66) Conservatives
 efforts to decrease the amount of government
    regulation are of little benefit to the average man. For one thing, only
    a fraction of the regulations can be eliminated because most regulations are
    necessary. For another thing, most of the deregulation affects business
    rather than the average individual, sM
o that its main effect is to take power
    from the government and give it to private corporations. What this means for
    the average man is that government interference in his life is replaced by
    interference from big corporations, which may be permitted, for example, to
    dump more chemicals that get into his water supply and give him cancer. The
    conservatives are just taking the average man for a sucker, exploiting his
    resentment of Big Government to promote the power of Big Business.
 (Paragraph 73) When someone approves of the purpose for which propaganda is
    being used in a given case, he generally calls it
    it some similar euphemism. But propaganda is propaganda regardless of the
    purpose for which it is used.
15. (Paragraph 83) We are not expressing approval or disapproval of the Panama
    invasion. We only use it to illustrate a point.
16. (Paragraph 95) When the American colonies were under British rule there were
    fewer and less effectiveM
 legal guarantees of freedom than there were after
    the American Constitution went into effect, yet there was more personal
    freedom in pre-industrial America, both before and after the War of
    Independence, than there was after the Industrial Revolution took hold in
    this country. We quote from
Violence in America: Historical and Comparative
 edited by Hugh Davis Graham and Ted Robert Gurr, Chapter 12
    by Roger Lane, pages 476-478:
The progressive heighteningM
 of standards of propriety, and with it the
    increasing reliance on official law enforcement (in 19th century America) ...
    were common to the whole society.... [T]he change in social behavior is so long
    term and so widespread as to suggest a connection with the most fundamental of
    contemporary social processes; that of industrial urbanization
Massachusetts in 1835 had a population of some 660,940, 81 percent
    rural, overwhelmingly preindustrial and native born. It
    considerable personal freedom. Whether teamsters, farmers or artisans, they were
    all accustomed to setting their own schedules, and the nature of their work made
    them physically independent of each other.... Individual problems, sins or even
    crimes, were not generally cause for wider social concern....
    the twin movements to the city and to the factory, both just gathering force in
    1835, had a progressive effect on personal behavior throughout the 19tM
    and into the 20th. The factory demanded regularity of behavior, a life governed
    by obedience to the rhythms of clock and calendar, the demands of foreman and
    supervisor. In the city or town, the needs of living in closely packed
    neighborhoods inhibited many actions previously unobjectionable. Both blue- and
    white-collar employees in larger establishments were mutually dependent on their
    fellows; as one man
s work fit into anther
The results of the new organization of life and work were apparent by 1900,
    when some 76 percent of the 2,805,346 inhabitants of Massachusetts were
    classified as urbanites. Much violent or irregular behavior which had been
    tolerable in a casual, independent society was no longer acceptable in the more
    formalized, cooperative atmosphere of the later period.... The move to the
    cities had, in short, produced a more tractable, more socialized, more
eneration than its predecessors.
17. (Paragraph 117) Apologists for the system are fond of citing cases in which
    elections have been decided by one or two votes, but such cases are rare.
18. (Paragraph 119)
Today, in technologically advanced lands, men live very
    similar lives in spite of geographical, religious, and political
    differences. The daily lives of a Christian bank clerk in Chicago,
    a Buddhist bank clerk in Tokyo, and a Communist bank clerk in Moscow are far
    more alike thanM
 the life of any one of them is like that of any single man
    who lived a thousand years ago. These similarities are the result of
    a common technology....
 L. Sprague de Camp,
The Ancient Engineers,
    Ballantine edition, page 17.
    The lives of the three bank clerks are not IDENTICAL. Ideology does have SOME
    effect. But all technological societies, in order to survive, must evolve along
    APPROXIMATELY the same trajectory.
19. (Paragraph 123) Just think an irresponsible genetic enM
gineer might create
    a lot of terrorists.
20. (Paragraph 124) For a further example of undesirable consequences of medical
    progress, suppose a reliable cure for cancer is discovered. Even if the
    treatment is too expensive to be available to any but the elite, it will
    greatly reduce their incentive to stop the escape of carcinogens into the
21. (Paragraph 128) Since many people may find paradoxical the notion that
    a large number of good things can add up to a bad thing, weM
    an analogy. Suppose Mr. A is playing chess with Mr. B. Mr. C, a Grand
    Master, is looking over Mr. A
s shoulder. Mr. A of course wants to win his
    game, so if Mr. C points out a good move for him to make, he is doing Mr.
    A a favor. But suppose now that Mr. C tells Mr. A how to make ALL of his
    moves. In each particular instance he does Mr. A a favor by showing him his
    best move, but by making ALL of his moves for him he spoils his game, since
    there is not point in M
s playing the game at all if someone else makes
    The situation of modern man is analogous to that of Mr. A. The system makes an
s life easier for him in innumerable ways, but in doing so it
    deprives him of control over his own fate.
22. (Paragraph 137) Here we are considering only the conflict of values within
    the mainstream. For the sake of simplicity we leave out of the picture
 values like the idea that wild nature is more importM
    human economic welfare.
23. (Paragraph 137) Self-interest is not necessarily MATERIAL self-interest. It
    can consist in fulfillment of some psychological need, for example, by
s own ideology or religion.
24. (Paragraph 139) A qualification: It is in the interest of the system to
    permit a certain prescribed degree of freedom in some areas. For example,
    economic freedom (with suitable limitations and restraints) has proved
    effective in promoting economic growth.M
 But only planned, circumscribed,
    limited freedom is in the interest of the system. The individual must always
    be kept on a leash, even if the leash is sometimes long (see paragraphs 94,
25. (Paragraph 143) We don
t mean to suggest that the efficiency or the
    potential for survival of a society has always been inversely proportional
    to the amount of pressure or discomfort to which the society subjects
    people. That certainly is not the case. There is good reason to believe that
    many primitive societies subjected people to less pressure than European
    society did, but European society proved far more efficient than any
    primitive society and always won out in conflicts with such societies
    because of the advantages conferred by technology.
26. (Paragraph 147) If you think that more effective law enforcement is
    unequivocally good because it suppresses crime, then remember that crime as
    defined by the system is not necessarily what YOU would call crime. Today,
 smoking marijuana is a
 and, in some places in the U.S., so is
    possession of an unregistered handgun. Tomorrow, possession of ANY firearm,
    registered or not, may be made a crime, and the same thing may happen with
    disapproved methods of child-rearing, such as spanking. In some countries,
    expression of dissident political opinions is a crime, and there is no
    certainty that this will never happen in the U.S., since no constitution or
    political system lasts forever.
If a society needs a large, powerful law enforcement establishment, then there
    is something gravely wrong with that society; it must be subjecting people to
    severe pressures if so many refuse to follow the rules, or follow them only
    because forced. Many societies in the past have gotten by with little or no
    formal law- enforcement.
27. (Paragraph 151) To be sure, past societies have had means of influencing
    human behavior, but these have been primitive and of low effectiveness
d with the technological means that are now being developed.
28. (Paragraph 152) However, some psychologists have publicly expressed opinions
    indicating their contempt for human freedom. And the mathematician Claude
    Shannon was quoted in Omni (August 1987) as saying,
I visualize a time when
    we will be to robots what dogs are to humans, and I
29. (Paragraph 154) This is no science fiction! After writing paragraph 154 we
    came across an article in ScientifM
ic American according to which scientists
    are actively developing techniques for identifying possible future criminals
    and for treating them by a combination of biological and psychological
    means. Some scientists advocate compulsory application of the treatment,
    which may be available in the near future. (See
Seeking the Criminal
 by W. Wayt Gibbs, Scientific American, March 1995.) Maybe you
    think this is OK because the treatment would be applied to those who might
ecome violent criminals. But of course it won
t stop there. Next,
    a treatment will be applied to those who might become drunk drivers (they
    endanger human life too), then perhaps to peel who spank their children,
    then to environmentalists who sabotage logging equipment, eventually to
    anyone whose behavior is inconvenient for the system.
30. (Paragraph 184) A further advantage of nature as a counter-ideal to
    technology is that, in many people, nature inspires the kind of reverence
at is associated with religion, so that nature could perhaps be idealized
    on a religious basis. It is true that in many societies religion has served
    as a support and justification for the established order, but it is also
    true that religion has often provided a basis for rebellion. Thus it may be
    useful to introduce a religious element into the rebellion against
    technology, the more so because Western society today has no strong
    religious foundation. Religion, nowadays either is used M
    transparent support for narrow, short-sighted selfishness (some
    conservatives use it this way), or even is cynically exploited to make easy
    money (by many evangelists), or has degenerated into crude irrationalism
    (fundamentalist protestant sects,
), or is simply stagnant
    (Catholicism, main-line Protestantism). The nearest thing to a strong,
    widespread, dynamic religion that the West has seen in recent times has been
    the quasi-religion of leftism, but leftisM
m today is fragmented and has no
    clear, unified, inspiring goal.
    Thus there is a religious vacuum in our society that could perhaps be filled by
    a religion focused on nature in opposition to technology. But it would be
    a mistake to try to concoct artificially a religion to fill this role. Such an
    invented religion would probably be a failure. Take the
    example. Do its adherents REALLY believe in it or are they just play-acting? If
    they are just play-actiM
ng their religion will be a flop in the end.
    It is probably best not to try to introduce religion into the conflict of nature
    vs. technology unless you REALLY believe in that religion yourself and find that
    it arouses a deep, strong, genuine response in many other people.
31. (Paragraph 189) Assuming that such a final push occurs. Conceivably the
    industrial system might be eliminated in a somewhat gradual or piecemeal
    fashion (see paragraphs 4, 167 and Note 4).
32. (Paragraph 193) It M
is even conceivable (remotely) that the revolution might
    consist only of a massive change of attitudes toward technology resulting in
    a relatively gradual and painless disintegration of the industrial system.
    But if this happens we
ll be very lucky. It
s far more probably that the
    transition to a nontechnological society will be very difficult and full of
    conflicts and disasters.
33. (Paragraph 195) The economic and technological structure of a society are
    far more important than M
its political structure in determining the way the
    average man lives (see paragraphs 95, 119 and Notes 16, 18).
34. (Paragraph 215) This statement refers to our particular brand of anarchism.
    A wide variety of social attitudes have been called
    be that many who consider themselves anarchists would not accept our
    statement of paragraph 215. It should be noted, by the way, that there is
    a nonviolent anarchist movement whose members probably would not accept FC
 as anarchist and certainly would not approve of FC
35. (Paragraph 219) Many leftists are motivated also by hostility, but the
    hostility probably results in part from a frustrated need for power.
36. (Paragraph 229) It is important to understand that we mean someone who
    sympathizes with these MOVEMENTS as they exist today in our society. One who
    believes that women, homosexuals, etc., should have equal rights is not
    necessary a leftist. The feminist, gay rights, etc., moveM
ments that exist in
    our society have the particular ideological tone that characterizes leftism,
    and if one believes, for example, that women should have equal rights it
    does not necessarily follow that one must sympathize with the feminist
    movement as it exists today.
If copyright problems make it impossible for this long quotation to be printed,
then please change Note 16 to read as follows:
16. (Paragraph 95) When the American colonies were under British rule there were
 effective legal guarantees of freedom than there were after
    the American Constitution went into effect, yet there was more personal
    freedom in pre-industrial America, both before and after the War of
    Independence, than there was after the Industrial Revolution took hold in
    this country. In
Violence in America: Historical and Comparative
 edited by Hugh Davis Graham and Ted Robert Gurr, Chapter 12
    by Roger Lane, it is explained how in pre-industrial America the averaL
    person had greater independence and autonomy than he does today, and how the
    process of industrialization necessarily led to the restriction of personal
SjLP1ero q puso nonfinancial data en bloque es 1ero q dijo
c/Foundry USA Pool #dropgold/
IjGREFUND:A1160A936F21C75928B8CB609A64DB506EEDAC042200A18094A68E7E385D9A2B
LjJProcertif:578a55eff2b5a508af6533a09449ee3b0fd200cec5f2688dc8f004490ce3bc87
&"""""""""""""""""""""""""""""""""""""""r
text/plain;charset=utf-8
text/html;charset=utf-8
    "title": "Ordinal Loops",
    "description": "Do Not Fiat",
    "storyline": "First chapter is observing the hostile FIAT environment in which Bitcoin has been born from the ashes of GFC. Such a contemporary landscape remains a living organism - loop. Furiously, Bitcoin ASCII fights the army of five main government currencies - ad infinitum. Conceptualization starts with 'Object 0' being a circular donut of currency interaction. Over time, these objects are deciphered into abstraction [Object 1 tM
o 5] and the 'Object 6' has the Bitcoin logo highlighted and colour coded demonstrating adversity against the government issued currency.",
    "website": "https://www.ordinalloops.xyz/",
    "twitter": "https://twitter.com/Ordinal_Loops"
{	"Object":	"0"	"inscription": 	"c2fe83b53f4eb0b8ba2b4748884727887f840332ef02f3f79b455fcf3a3d11ebi0",	}
{	"Object":	"1"	"inscription": 	"2e785256005b2aee00fd22038d99891e0026d2620e3c4180c9c2d9706fe4b364i0",	}
{	"Object":	"2"	"inscription": 	"dd1f515b828eedabd6b0M
be147cf611ca08c20f39058feee9b96efaa2eba43d9di0",	}
{	"Object":	"3"	"inscription": 	"821eec8cf7672e232aa34998d0a92e638decd5aab9f36c30bccdaedec662a829i0",	}
{	"Object":	"4"	"inscription": 	"fe00c5cb1d042621025b1a9bbb36d682607f0efe1182ec5109840d3fccd7ce4di0",	}
{	"Object":	"5"	"inscription": 	"9c5c6e9b6769fd5eda0aef192d9e39a6133b28ffb3ea2ada5e40b04266485e5bi0",	}
{	"Object":	"6"	"inscription": 	"51db0e3702a0679e8adedeab75d58bc1cabfc5f3d53ff6f59e07de62365dc124i0",	}
6j4ion:1.QmP72xL8L516W516ihb8dJxkusim5SqHsqovAXfg5mhMgL
Aj?=:ETH.ETH:0xdCb7C510423E8c197FD8503d161a9cc4F462086c:38300:te:0
)j'11hLbkAPQzqhohneXthhouZy16KQXmUMGGqGrRh
FjDOUT:D139DEC2EABAA85DD8DC25C7F1BFA35B7657CE445794F6885F8FE83965EAC0A8
FjDOUT:D7C3ED8D9992C353263C9ECD38B3C73B2BC8D8212A456E68A6427C47B293EBC0
CjA=:AVAX.AVAX:0xe66fbc8be19bd6b60a7fdc743a1019435ee0a04f:34722901:t
!22222222222222222222222222222222222222222222222222
1\ Powered by Luxor Tech \
IjGREFUND:16FF38E52C23A71AD390541B9339499F5449CFE3FACB034ABAA8AE02329B73D7
Ahttp://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.5.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" photoshop:ColorMode="3" photoshop:ICCProfile="M
sRGB IEC61966-2.1" xmp:ModifyDate="2023-02-02T18:08:59-05:00" xmp:MetadataDate="2023-02-02T18:08:59-05:00"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="produced" stEvt:softwareAgent="Affinity Photo 1.10.6" stEvt:when="2023-02-02T18:08:59-05:00"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                                       M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                  <?xpacket end="w"?>
 .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQRO
&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
cropWhenPrintingbool
http://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about=M
"" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-01T14:55:30+03:00" xmp:ModifyDate="2023-02-01T14:55:30+03:00" dc:format="image/jpeg" xmpMM:M
InstanceID="xmp.iid:29c0327d-f970-8344-90bf-be98edc57fc5" xmpMM:DocumentID="adobe:docid:photoshop:d288bca7-b69c-fa45-9c20-27c80addcac8" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stM
Evt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:f1d9e2a9-67df-2e4f-a24f-e2f0dee4dcdd" stEvt:when="2023-02-01T14:55:30+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvtM
:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:29c0327d-f970-8344-90bf-be98edc57fc5" stEvt:when="2023-02-01T14:55:30+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:f1d9e2a9-67df-2e4f-a24f-e2f0dee4dcdd" stRef:documentID="adobe:docid:photoshop:7827a9af-d813-f04d-b2d1-dd034885cb76" stRef:originalDocumentID="xmp.did:8653de18-59b9-7M
643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                                                                                                                                                                 M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                        <?xpacket end="w"?>
Copyright (c) 1998 Hewlett-Packard Company
IEC http://www.iec.ch
IEC http://www.iec.ch
.IEC 61966-2.1 Default RGB colour space - sRGB
.IEC 61966-2.1 Default RGB colour space - sRGB
,Reference Viewing Condition in IEC61966-2.1
,Reference Viewing Condition in IEC61966-2.1
EzTXtRaw profile type exif
iTXtXML:com.adobe.xmp
" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2"M
 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <rdf:Description rdf:about=""
    xmlns:aux="http://ns.adobe.com/exif/1.0/aux/"
   aux:SerialNumber="20f3f16cb9daf2995495936439d4c8e848849c0c4f91f20e9a0939334ccb1225"/>


                                                     M





                                                                    M





                                                                                   M




                                                                                                  M



'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
  <title>Bitcoin Face</title>
  <g style="isolation:isolate"><rect id="Background-2" width="1025" height="1025" style="fill:#3d4b8a" /><g id="Body-4"><path d="M504,494.22h0A412.66,412.66,0,0,1,916.61,906.88V1003a0,0,0,0,1,0,0H91.3a0,0,0,0,1,0,0V906.88A412.66,412.66,0,0,1,504,494.22Z" style="fill:#6c1908" /></g><g id="Head-11"><path d="M164.72,490.82C164.72,683.66,317,840,504.85,840S845,683.66,845,490.82" transform="translate(0.28)" style="fill:#f5b659M
" /><path d="M186,490.82c0,180.79,142.76,327.34,318.87,327.34S823.71,671.61,823.71,490.82" transform="translate(0.28)" style="fill:#ecdea0" /><path d="M228.5,490.82c0,156.68,123.72,283.7,276.35,283.7s276.34-127,276.34-283.7" transform="translate(0.28)" style="fill:#e18d27" /><polygon points="506.79 774.52 503.45 774.52 503.45 510.93 781.47 510.93 781.47 514.36 506.79 514.36 506.79 774.52" style="fill:#ecdea0" /><polygon points="695.33 688.51 497.93 508.38 760.74 598.31 759.68 601.56 512.31 516.91 697.55 685.94 695.M
33 688.51" style="fill:#ecdea0" /><polygon points="609.89 753.4 503.6 513.35 506.64 511.94 612.93 751.98 609.89 753.4" style="fill:#ecdea0" /><polygon points="506.79 774.52 503.45 774.52 503.45 514.36 228.77 514.36 228.77 510.93 506.79 510.93 506.79 774.52" style="fill:#ecdea0" /><polygon points="314.91 688.51 312.69 685.94 497.93 516.91 250.56 601.56 249.5 598.31 512.31 508.38 314.91 688.51" style="fill:#ecdea0" /><polygon points="400.35 753.4 397.31 751.98 503.6 511.94 506.64 513.35 400.35 753.4" style="fill:#ecdM
ea0" /><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M664.28,601.65A12.63,12.63,0,1,1,676.58,589,12.47,12.47,0,0,1,664.28,601.65Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,664.28,579.83Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M643,579.83a12.63,12.63,0,1,1,12.3-12.63A12.49,12.49,0,0,1,643,579.83ZM643,558a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,643,558Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-bM
lend-mode:overlay"><path d="M738.68,579.83c-12.64,0-22.93-10.56-22.93-23.54s10.29-23.54,22.93-23.54,22.92,10.56,22.92,23.54S751.32,579.83,738.68,579.83Zm0-43.65c-10.8,0-19.59,9-19.59,20.11s8.79,20.11,19.59,20.11,19.58-9,19.58-20.11S749.48,536.18,738.68,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M685.53,536.18a12.63,12.63,0,1,1,12.3-12.63A12.47,12.47,0,0,1,685.53,536.18Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.08,9.08,0,0,0,685.53,514.36Z" transformM
="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M483.59,732.59c-12.64,0-22.93-10.56-22.93-23.54s10.29-23.54,22.93-23.54,22.93,10.56,22.93,23.54S496.23,732.59,483.59,732.59Zm0-43.65c-10.8,0-19.59,9-19.59,20.11s8.79,20.11,19.59,20.11,19.58-9,19.58-20.11S494.39,688.94,483.59,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M451.7,688.94A12.63,12.63,0,1,1,464,676.32,12.48,12.48,0,0,1,451.7,688.94M
Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,451.7,667.12Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M387.93,601.65A12.63,12.63,0,1,1,400.23,589,12.48,12.48,0,0,1,387.93,601.65Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,387.93,579.83Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M324.16,558a12.63,12.63,0,1,1,12.3-12.62A12.47,12.47,0,0,1,324.16,558Zm0-21.82a9.2,9.2,0,1,0,9,9.2AM
9.09,9.09,0,0,0,324.16,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M260.38,558a12.63,12.63,0,1,1,12.3-12.62A12.47,12.47,0,0,1,260.38,558Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,260.38,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M292.27,645.3c-12.64,0-22.93-10.56-22.93-23.54s10.29-23.54,22.93-23.54,22.93,10.56,22.93,23.54S304.91,645.3,292.27,645.3Zm0-43.65c-10.8M
,0-19.59,9-19.59,20.11s8.79,20.11,19.59,20.11,19.59-9,19.59-20.11S303.07,601.65,292.27,601.65Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M324.16,667.12a12.63,12.63,0,1,1,12.3-12.63A12.48,12.48,0,0,1,324.16,667.12Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,324.16,645.3Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M600.5,667.12a12.63,12.63,0,1,1,12.3-12.63A12.48,12.48,0,0,1,600.M
5,667.12Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.08,9.08,0,0,0,600.5,645.3Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M664.28,710.76a12.63,12.63,0,1,1,12.3-12.62A12.47,12.47,0,0,1,664.28,710.76Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,664.28,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M611.13,732.59c-12.64,0-22.92-10.56-22.92-23.54s10.28-23.54,22.92-23.54,22.93,10.56,22.93,23M
.54S623.77,732.59,611.13,732.59Zm0-43.65c-10.8,0-19.58,9-19.58,20.11s8.78,20.11,19.58,20.11,19.59-9,19.59-20.11S621.94,688.94,611.13,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M706.79,645.3a12.63,12.63,0,1,1,12.3-12.63A12.49,12.49,0,0,1,706.79,645.3Zm0-21.83a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,706.79,623.47Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M473,579.83a12.63,12.M
63,0,1,1,12.3-12.63A12.49,12.49,0,0,1,473,579.83ZM473,558a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,473,558Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M430.45,536.18a12.63,12.63,0,1,1,12.29-12.63A12.48,12.48,0,0,1,430.45,536.18Zm0-21.82a9.2,9.2,0,1,0,8.95,9.19A9.09,9.09,0,0,0,430.45,514.36Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M579.25,558a12.63,12.63,0,1,1,12.3-12.62A12.48,12.48M
,0,0,1,579.25,558Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,579.25,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M558,601.65A12.63,12.63,0,1,1,570.29,589,12.48,12.48,0,0,1,558,601.65Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,558,579.83Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M494.22,649.82a17.15,17.15,0,1,1,16.7-17.15A17,17,0,0,1,494.22,649.82Zm0-30.87a13.72,13.72M
,0,1,0,13.36,13.72A13.56,13.56,0,0,0,494.22,619Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M536.73,710.76A12.63,12.63,0,1,1,549,698.14,12.48,12.48,0,0,1,536.73,710.76Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,536.73,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M558,758.93a17.15,17.15,0,1,1,16.7-17.15A16.95,16.95,0,0,1,558,758.93Zm0-30.86a13.72,13.72,0,1,0,13.36,13.71A13M
.55,13.55,0,0,0,558,728.07Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M366.67,710.76A12.63,12.63,0,1,1,379,698.14,12.48,12.48,0,0,1,366.67,710.76Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,366.67,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><rect x="164.72" y="490.82" width="680.24" height="21.82" transform="translate(1009.97 1003.46) rotate(-180)" style="fill:#aea4b3;opacity:0.52;mix-blend-mode:overlay" /><path d="M845.81,50M
7.17C845.81,314.33,693.53,158,505.69,158S165.57,314.33,165.57,507.17" transform="translate(0.28)" style="fill:#f5b659" /><path d="M824.55,507.17c0-180.79-142.76-327.35-318.86-327.35S186.82,326.38,186.82,507.17" transform="translate(0.28)" style="fill:#ecdea0" /><path d="M782,507.17c0-156.69-123.73-283.7-276.35-283.7s-276.35,127-276.35,283.7" transform="translate(0.28)" style="fill:#e18d27" /><polygon points="504.29 223.47 507.63 223.47 507.63 487.06 229.62 487.06 229.62 483.63 504.29 483.63 504.29 223.47" style="fiM
ll:#ecdea0" /><polygon points="315.76 309.48 513.15 489.61 250.35 399.68 251.4 396.43 498.77 481.07 313.54 312.04 315.76 309.48" style="fill:#ecdea0" /><polygon points="401.19 244.58 507.49 484.63 504.44 486.05 398.15 246 401.19 244.58" style="fill:#ecdea0" /><polygon points="504.29 223.47 507.63 223.47 507.63 483.63 782.31 483.63 782.31 487.06 504.29 487.06 504.29 223.47" style="fill:#ecdea0" /><polygon points="696.17 309.48 698.39 312.04 513.15 481.07 760.53 396.43 761.58 399.68 498.77 489.61 696.17 309.48" styleM
="fill:#ecdea0" /><polygon points="610.73 244.58 613.77 246 507.49 486.05 504.44 484.63 610.73 244.58" style="fill:#ecdea0" /><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M346.26,396.34A12.63,12.63,0,1,1,334,409,12.48,12.48,0,0,1,346.26,396.34Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,346.26,418.16Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M367.51,418.16a12.63,12.63,0,1,1-12.29,12.63A12.48,12.48,0,0,1,367.51,418.16Zm0,21.82a9.2,9.2M
,0,1,0-8.95-9.19A9.08,9.08,0,0,0,367.51,440Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M271.85,418.16c12.65,0,22.93,10.56,22.93,23.54s-10.28,23.54-22.93,23.54-22.92-10.56-22.92-23.54S259.21,418.16,271.85,418.16Zm0,43.65c10.81,0,19.59-9,19.59-20.11s-8.78-20.11-19.59-20.11-19.58,9-19.58,20.11S261.05,461.81,271.85,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M325,461.81a12.63M
,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,325,461.81Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,325,483.63Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M527,265.4c12.64,0,22.92,10.56,22.92,23.54S539.59,312.48,527,312.48,504,301.92,504,288.94,514.3,265.4,527,265.4Zm0,43.65c10.8,0,19.58-9,19.58-20.11s-8.78-20.11-19.58-20.11-19.59,9-19.59,20.11S516.15,309.05,527,309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mM
ix-blend-mode:overlay"><path d="M558.83,309.05a12.63,12.63,0,1,1-12.3,12.62A12.47,12.47,0,0,1,558.83,309.05Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,558.83,330.87Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M622.61,396.34A12.63,12.63,0,1,1,610.31,409,12.47,12.47,0,0,1,622.61,396.34Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,622.61,418.16Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><M
path d="M686.38,440a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,686.38,440Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,686.38,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M750.15,440a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,750.15,440Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,750.15,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M718.26,352.69c12.65,0,22.93M
,10.56,22.93,23.54s-10.28,23.54-22.93,23.54-22.92-10.56-22.92-23.54S705.62,352.69,718.26,352.69Zm0,43.65c10.81,0,19.59-9,19.59-20.11s-8.78-20.11-19.59-20.11-19.58,9-19.58,20.11S707.46,396.34,718.26,396.34Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M686.38,330.87a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,686.38,330.87Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,686.38,352.69Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="M
opacity:0.52;mix-blend-mode:overlay"><path d="M410,330.87a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,410,330.87Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,410,352.69Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M346.26,287.22A12.63,12.63,0,1,1,334,299.85,12.49,12.49,0,0,1,346.26,287.22Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,346.26,309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlM
ay"><path d="M399.4,265.4c12.64,0,22.93,10.56,22.93,23.54S412,312.48,399.4,312.48s-22.93-10.56-22.93-23.54S386.76,265.4,399.4,265.4Zm0,43.65c10.8,0,19.59-9,19.59-20.11s-8.79-20.11-19.59-20.11-19.59,9-19.59,20.11S388.6,309.05,399.4,309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M303.74,352.69a12.63,12.63,0,1,1-12.3,12.63A12.48,12.48,0,0,1,303.74,352.69Zm0,21.82a9.2,9.2,0,1,0-9-9.19A9.09,9.09,0,0,0,303.74,374.51Z" transform="translate(0.28)" styM
le="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M537.58,418.16a12.63,12.63,0,1,1-12.3,12.63A12.47,12.47,0,0,1,537.58,418.16Zm0,21.82a9.2,9.2,0,1,0-9-9.19A9.08,9.08,0,0,0,537.58,440Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M580.09,461.81a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,580.09,461.81Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,580.09,483.63Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><gM
 style="opacity:0.52;mix-blend-mode:overlay"><path d="M431.29,440A12.63,12.63,0,1,1,419,452.61,12.49,12.49,0,0,1,431.29,440Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,431.29,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M452.54,396.34A12.63,12.63,0,1,1,440.25,409,12.47,12.47,0,0,1,452.54,396.34Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,452.54,418.16Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blendM
-mode:overlay"><path d="M516.32,348.17a17.15,17.15,0,1,1-16.7,17.15A16.95,16.95,0,0,1,516.32,348.17Zm0,30.86A13.72,13.72,0,1,0,503,365.32,13.55,13.55,0,0,0,516.32,379Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M473.8,287.22a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,473.8,287.22Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,473.8,309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><patM
h d="M452.54,239.06a17.15,17.15,0,1,1-16.7,17.14A17,17,0,0,1,452.54,239.06Zm0,30.86a13.72,13.72,0,1,0-13.36-13.72A13.56,13.56,0,0,0,452.54,269.92Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M643.86,287.22a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,643.86,287.22Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,643.86,309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><rect x="165.84" y="485.34" width="680.24" height="21.82" style="fillM
:#aea4b3;opacity:0.52;mix-blend-mode:overlay" /></g><g id="Face-Accessory-1"><circle cx="516.52" cy="942.46" r="62.02" style="fill:#f96020" /><circle cx="516.52" cy="942.46" r="54.28" style="fill:#d74816" /><rect x="487.05" y="879.79" width="35.52" height="96.79" transform="translate(214.46 1954.46) rotate(-135)" style="fill:#ffa520" /><circle cx="510.95" cy="942.07" r="66.36" style="fill:#f96020" /><circle cx="510.94" cy="942.07" r="58.08" style="fill:#d74816" /><g id="_04CbBX.tif" data-name="04CbBX.tif"><path d="M
M499.36,875.6h5.23a5.28,5.28,0,0,0,1.13.08c1.51.13,3,.28,4.52.52a52.15,52.15,0,0,1,10.43,2.72,53.73,53.73,0,0,1,32.17,33.55,52.41,52.41,0,0,1,2.21,10c.15,1.22.26,2.45.31,3.67a1.63,1.63,0,0,0,.08.82v4.31a1.52,1.52,0,0,0-.08.82,46.85,46.85,0,0,1-.52,5.14,52,52,0,0,1-5.39,16.51,53.57,53.57,0,0,1-43.89,28.75,50.62,50.62,0,0,1-8-.07,52.07,52.07,0,0,1-10.21-1.83,53.13,53.13,0,0,1-29.73-21.46,53,53,0,0,1-9.05-25.57,17,17,0,0,0-.16-1.88c0-.61,0-1.22,0-1.83,0-1.1,0-2.2,0-3.3a.77.77,0,0,0,.07-.4,53.09,53.09,0,0,1,5.25-20.37,M
53.67,53.67,0,0,1,35.15-28.6,54.71,54.71,0,0,1,9.34-1.48A8.57,8.57,0,0,0,499.36,875.6Zm-42,53.5a44.61,44.61,0,1,0,44.59-44.62A44.59,44.59,0,0,0,457.33,929.1Z" transform="translate(9.03 13)" style="fill:#ff9317" /><path d="M515.36,927.51a21.52,21.52,0,0,1,5.48,1.69,11.83,11.83,0,0,1,5.27,4.7,12,12,0,0,1,1.52,4.64,17.76,17.76,0,0,1-.27,6.21,10.87,10.87,0,0,1-6.8,8,25.21,25.21,0,0,1-5.77,1.53c-1.34.21-2.68.35-4,.44-.47,0-.47,0-.47.51v7.94c0,.5,0,.51-.51.51H504c-.56,0-.53,0-.53-.53v-7.68c0-.64.05-.57-.58-.57h-5.8c-.46,M
0-.46,0-.47.48v7.73c0,.59,0,.57-.54.57h-5.74c-.55,0-.55,0-.55-.57,0-2.51,0-5,0-7.53,0-.14,0-.27,0-.41s-.06-.29-.26-.27H476.43c-.24,0-.34-.06-.34-.31,0-1.9,0-3.79,0-5.69,0-.27.11-.35.36-.35,1.18,0,2.36,0,3.54,0a10.18,10.18,0,0,0,2.13-.27,1.92,1.92,0,0,0,1.55-1.57,11.89,11.89,0,0,0,.29-2.85V921.52c0-2.47,0-4.95,0-7.43a13.56,13.56,0,0,0-.27-2.69,2,2,0,0,0-1.71-1.68,13.39,13.39,0,0,0-2.75-.24h-2.66c-.47,0-.47,0-.47-.49,0-1.76,0-3.51,0-5.27,0-.6-.06-.56.54-.57h12.55c.57,0,.57,0,.58-.54v-7.89c0-.2.09-.28.28-.26h6c.61,0,.M
56-.06.56.54,0,2.51,0,5,0,7.53,0,.7-.05.62.64.62H503c.46,0,.46,0,.47-.49v-7.94c0-.2.07-.28.27-.26h6c.59,0,.57-.09.57.58v7.84c0,.39,0,.39.39.42a32.44,32.44,0,0,1,6.11,1,17.64,17.64,0,0,1,3.2,1.2,9.17,9.17,0,0,1,5,6.06,14.22,14.22,0,0,1,.12,7.24,10.31,10.31,0,0,1-4.17,6,18.31,18.31,0,0,1-5.34,2.53C515.55,927.36,515.43,927.34,515.36,927.51Zm-18.76,12.4c0,2.77,0,5.53,0,8.3,0,.44,0,.45.44.45h3a44,44,0,0,0,6.39-.31,15,15,0,0,0,3.66-.93,6.31,6.31,0,0,0,3.8-4.16,12,12,0,0,0,.26-6,6.32,6.32,0,0,0-2.8-4.24A9.11,9.11,0,0,0,50M
8.7,932a23.93,23.93,0,0,0-4.61-.65c-2.39-.14-4.78,0-7.17-.08-.26,0-.34.1-.32.33s0,.24,0,.36Zm0-22.76v4.25c0,1.05,0,2.09,0,3.13,0,.25,0,.37.34.37a68.58,68.58,0,0,0,7.72-.23,14.47,14.47,0,0,0,3.67-.89,5.57,5.57,0,0,0,3.45-4A11.75,11.75,0,0,0,512,915a5.49,5.49,0,0,0-3.83-4.58,18.25,18.25,0,0,0-5-.88c-2-.1-4,0-6,0h-.21c-.17,0-.25.08-.24.25s0,.27,0,.41Z" transform="translate(9.03 13)" style="fill:#ff9317" /></g><circle cx="459.8" cy="882.7" r="13.97" style="fill:#ff9317" /><circle cx="438.84" cy="861.74" r="13.97" styleM
="fill:#ff9317" /><circle cx="417.89" cy="840.79" r="13.97" style="fill:#ff9317" /><circle cx="396.93" cy="819.83" r="13.97" style="fill:#ff9317" /><circle cx="564.57" cy="882.7" r="13.97" style="fill:#f96020" /><circle cx="585.53" cy="861.74" r="13.97" style="fill:#f96020" /><circle cx="606.48" cy="840.79" r="13.97" style="fill:#f96020" /><circle cx="627.44" cy="819.83" r="13.97" style="fill:#f96020" /></g><g id="Earrings-1"><circle cx="141.68" cy="475.44" r="61.24" style="fill:#f26227" /><circle cx="141.68" cy="4M
75.44" r="53.6" style="fill:#d74b27" /><g><path d="M134.36,420.84h4.82a6.06,6.06,0,0,0,1,.07c1.4.12,2.79.26,4.18.48A48.47,48.47,0,0,1,154,423.9a49.56,49.56,0,0,1,29.69,31,47.44,47.44,0,0,1,2,9.2c.13,1.13.24,2.25.28,3.39a1.48,1.48,0,0,0,.07.75v4a1.37,1.37,0,0,0-.07.75,42.22,42.22,0,0,1-.48,4.75,47.88,47.88,0,0,1-5,15.23,49.3,49.3,0,0,1-47.87,26.48,47.84,47.84,0,0,1-9.43-1.7,49.07,49.07,0,0,1-27.44-19.8,49.09,49.09,0,0,1-8.35-23.6,12.49,12.49,0,0,0-.15-1.73c0-.57,0-1.13,0-1.69,0-1,0-2,0-3.05a.57.57,0,0,0,.07-.36,47.0M
7,47.07,0,0,1,.76-6.38,48.23,48.23,0,0,1,4.09-12.43,49.32,49.32,0,0,1,41.06-27.75A9.08,9.08,0,0,0,134.36,420.84ZM95.57,470.21A41.17,41.17,0,1,0,136.72,429,41.12,41.12,0,0,0,95.57,470.21Z" transform="translate(4.96 5.26)" style="fill:#f79421" /><path d="M149.13,468.75a19.6,19.6,0,0,1,5.05,1.56,10.88,10.88,0,0,1,4.87,4.33,11.08,11.08,0,0,1,1.4,4.28,16.24,16.24,0,0,1-.25,5.73,10.06,10.06,0,0,1-6.28,7.4,23.28,23.28,0,0,1-5.33,1.41c-1.23.19-2.46.33-3.71.4-.43,0-.44,0-.44.48v7.33c0,.46,0,.46-.47.46h-5.35c-.52,0-.49,0-.49M
-.48v-7.09c0-.59.05-.53-.54-.53h-5.34c-.43,0-.43,0-.43.44,0,2.38,0,4.76,0,7.14,0,.55.05.52-.5.52H126c-.5,0-.51,0-.51-.52v-7.33c0-.17-.06-.27-.24-.25s-.26,0-.38,0H113.19c-.21,0-.31,0-.31-.29q0-2.62,0-5.25c0-.25.1-.32.34-.31h3.26a9.33,9.33,0,0,0,2-.26,1.76,1.76,0,0,0,1.43-1.45,11,11,0,0,0,.26-2.63q0-10.31,0-20.61c0-2.29,0-4.57,0-6.86a11.52,11.52,0,0,0-.24-2.48,1.88,1.88,0,0,0-1.58-1.55,12.77,12.77,0,0,0-2.54-.23c-.82,0-1.64,0-2.46,0-.43,0-.43,0-.43-.45v-4.87c0-.56,0-.52.5-.52H125c.52,0,.52,0,.52-.51v-6.9c0-.12,0-.25,M
0-.38s.09-.25.26-.24h5.54c.56,0,.51-.06.51.5,0,2.32,0,4.63,0,6.95,0,.65,0,.58.58.58h5.3c.43,0,.43,0,.43-.46v-7c0-.11,0-.22,0-.33s.07-.26.25-.25h5.54c.55,0,.52-.08.52.54V446c0,.36,0,.35.37.38a30.6,30.6,0,0,1,5.63.91,16.49,16.49,0,0,1,3,1.11,8.5,8.5,0,0,1,4.65,5.6,13.15,13.15,0,0,1,.11,6.68,9.54,9.54,0,0,1-3.85,5.55,17,17,0,0,1-4.93,2.34C149.3,468.61,149.19,468.58,149.13,468.75Zm-17.31,11.44c0,2.55,0,5.1,0,7.66,0,.41,0,.41.41.41H135a40.14,40.14,0,0,0,5.9-.28,14.22,14.22,0,0,0,3.38-.86,5.83,5.83,0,0,0,3.5-3.84,11,11,0M
,0,0,.24-5.51,5.78,5.78,0,0,0-2.58-3.91,8.31,8.31,0,0,0-2.49-1,21.66,21.66,0,0,0-4.25-.6c-2.2-.13-4.41,0-6.62-.07-.23,0-.31.09-.29.3s0,.22,0,.33Q131.81,476.5,131.82,480.19Zm0-21v3.92q0,1.44,0,2.88c0,.23.05.35.31.34a62.82,62.82,0,0,0,7.13-.2,13.67,13.67,0,0,0,3.39-.83,5.13,5.13,0,0,0,3.18-3.67,10.6,10.6,0,0,0,.15-4.45,5,5,0,0,0-3.52-4.22,16.7,16.7,0,0,0-4.64-.82c-1.86-.09-3.72,0-5.58,0H132a.2.2,0,0,0-.22.23v.38Q131.81,456,131.81,459.19Z" transform="translate(4.96 5.26)" style="fill:#f79421" /></g><circle cx="897.68"M
 cy="478.42" r="61.24" style="fill:#f26227" /><circle cx="897.68" cy="478.42" r="53.6" style="fill:#d74b27" /><g><path d="M890.36,423.81h4.82a5.2,5.2,0,0,0,1,.07c1.4.12,2.79.27,4.18.48a48.5,48.5,0,0,1,9.62,2.52,49.52,49.52,0,0,1,29.69,31,47.6,47.6,0,0,1,2,9.2c.13,1.13.24,2.26.28,3.39a1.52,1.52,0,0,0,.07.76v4a1.4,1.4,0,0,0-.07.76,41.81,41.81,0,0,1-.48,4.74,47.88,47.88,0,0,1-5,15.23,49.44,49.44,0,0,1-40.51,26.54,47.59,47.59,0,0,1-7.36-.06,48.57,48.57,0,0,1-9.43-1.69,49.12,49.12,0,0,1-27.44-19.8,49.17,49.17,0,0,1-8.35M
-23.6,12.63,12.63,0,0,0-.15-1.74c0-.56,0-1.12,0-1.69,0-1,0-2,0-3a.62.62,0,0,0,.07-.37,47.11,47.11,0,0,1,.76-6.37,48.1,48.1,0,0,1,4.09-12.43,49.49,49.49,0,0,1,32.44-26.4,50.07,50.07,0,0,1,8.62-1.36A7.27,7.27,0,0,0,890.36,423.81Zm-38.79,49.37A41.17,41.17,0,1,0,892.72,432,41.14,41.14,0,0,0,851.57,473.18Z" transform="translate(4.96 5.26)" style="fill:#f79421" /><path d="M905.13,471.72a19.6,19.6,0,0,1,5,1.56,10.85,10.85,0,0,1,4.87,4.34,11,11,0,0,1,1.4,4.28,16.25,16.25,0,0,1-.25,5.73,10,10,0,0,1-6.28,7.39,23.28,23.28,0,0M
,1-5.33,1.41c-1.23.19-2.46.33-3.71.41-.43,0-.44,0-.44.47v7.33c0,.46,0,.47-.47.47h-5.35c-.52,0-.49,0-.49-.49v-7.09c0-.59,0-.53-.54-.53h-5.34c-.43,0-.43,0-.43.45,0,2.38,0,4.76,0,7.14,0,.54.05.52-.5.52H882c-.5,0-.51,0-.51-.53v-7.33c0-.17-.06-.26-.24-.25H869.19c-.21,0-.31-.05-.31-.28q0-2.62,0-5.25c0-.26.1-.32.34-.32h3.26a9.33,9.33,0,0,0,2-.26,1.76,1.76,0,0,0,1.43-1.45,11,11,0,0,0,.26-2.62q0-10.32,0-20.62c0-2.28,0-4.57,0-6.85a11.55,11.55,0,0,0-.24-2.49,1.88,1.88,0,0,0-1.58-1.55,12.77,12.77,0,0,0-2.54-.23h-2.46c-.43,0-.4M
3,0-.43-.45v-4.87c0-.55,0-.52.5-.52H881c.52,0,.52,0,.52-.5v-6.91c0-.12,0-.25,0-.37s.09-.26.26-.25h5.54c.56,0,.51-.05.51.5,0,2.32,0,4.64,0,6.95,0,.65-.05.58.58.58h5.3c.43,0,.43,0,.43-.45v-7c0-.11,0-.22,0-.33s.07-.26.25-.25h5.54c.55,0,.52-.07.52.55V449c0,.36,0,.36.37.38a30.62,30.62,0,0,1,5.63.92,16.45,16.45,0,0,1,3,1.1,8.5,8.5,0,0,1,4.65,5.6,13.19,13.19,0,0,1,.11,6.69,9.49,9.49,0,0,1-3.85,5.54,16.79,16.79,0,0,1-4.93,2.34C905.3,471.58,905.19,471.56,905.13,471.72Zm-17.31,11.44c0,2.55,0,5.11,0,7.66,0,.41,0,.41.41.41H891M
a40.14,40.14,0,0,0,5.9-.29,14.2,14.2,0,0,0,3.38-.85,5.85,5.85,0,0,0,3.5-3.85,11,11,0,0,0,.24-5.51,5.78,5.78,0,0,0-2.58-3.91,8.31,8.31,0,0,0-2.49-1,23.27,23.27,0,0,0-4.25-.6c-2.2-.13-4.41,0-6.62-.07-.23,0-.31.09-.29.31s0,.22,0,.33C887.81,478.25,887.81,480.7,887.82,483.16Zm0-21v3.92c0,1,0,1.92,0,2.89,0,.22.05.34.31.33a62.82,62.82,0,0,0,7.13-.2,13.67,13.67,0,0,0,3.39-.83,5.11,5.11,0,0,0,3.18-3.66,10.65,10.65,0,0,0,.15-4.46,5,5,0,0,0-3.52-4.22,16.68,16.68,0,0,0-4.64-.81c-1.86-.1-3.72,0-5.58-.05H888c-.15,0-.23.07-.22.23M
v.38Q887.8,458.92,887.81,462.16Z" transform="translate(4.96 5.26)" style="fill:#f79421" /></g></g><g id="Ears-2"><circle cx="133.72" cy="393.44" r="61.91" style="fill:#883a62" /><circle cx="154.36" cy="469.11" r="41.27" style="fill:#883a62" /><circle cx="181.87" cy="427.84" r="41.27" style="fill:#542f5f" /><g style="opacity:0.5700000000000001;mix-blend-mode:overlay"><line x1="126.08" y1="359.82" x2="194.87" y2="428.6" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /><line x1="181.87" y1="436M
.67" x2="146.52" y2="472.03" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /><line x1="155.36" y1="392.49" x2="128.85" y2="419" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /><line x1="164.2" y1="401.32" x2="128.85" y2="436.67" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /></g><circle cx="119.85" cy="534.05" r="53.19" style="fill:#f96020" /><circle cx="119.84" cy="534.05" r="46.55" style="fill:#d74816" /><circle cx="890.1" cy="392.85" r="M
61.91" style="fill:#883a62" /><circle cx="869.47" cy="468.51" r="41.27" style="fill:#883a62" /><circle cx="841.95" cy="427.24" r="41.27" style="fill:#542f5f" /><g style="opacity:0.5700000000000001;mix-blend-mode:overlay"><line x1="897.75" y1="359.22" x2="828.96" y2="428.01" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /><line x1="841.95" y1="436.08" x2="877.31" y2="471.43" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /><line x1="868.47" y1="391.89" x2="894.98" y2=M
"418.41" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /><line x1="859.63" y1="400.73" x2="894.98" y2="436.08" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /></g><circle cx="903.98" cy="533.46" r="53.19" style="fill:#f96020" /><circle cx="903.99" cy="533.46" r="46.55" style="fill:#d74816" /></g><g id="Mouth-5"><polyline points="528.78 551.11 214.79 551.11 336.9 673.22 528.78 673.22 528.78 551.11" style="fill:#4a2955" /><rect x="441.56" y="562.74" width="23.26" heigM
ht="29.07" style="fill:#fdf6de" /><rect x="470.63" y="562.74" width="23.26" height="29.07" style="fill:#fdf6de" /><polygon points="406.67 603.44 429.93 562.74 429.93 562.74 406.67 562.74 406.67 603.44" style="fill:#fdf6de" /><polygon points="331.08 664.5 389.23 562.74 389.23 562.74 331.08 562.74 331.08 664.5" style="fill:#fdf6de" /><polygon points="302.84 591.81 319.45 562.74 319.45 562.74 302.84 562.74 302.84 591.81" style="fill:#fdf6de" /><polyline points="480.08 550.62 794.06 550.62 671.96 672.72 480.08 672.72 4M
80.08 550.62" style="fill:#161f26" /><rect x="545.7" y="562.24" width="23.26" height="29.07" transform="translate(1112.99 1153.56) rotate(-180)" style="fill:#fdf6de" /><rect x="516.62" y="562.24" width="23.26" height="29.07" transform="translate(1054.85 1153.56) rotate(-180)" style="fill:#fdf6de" /><rect x="487.55" y="562.24" width="23.26" height="29.07" transform="translate(996.7 1153.56) rotate(-180)" style="fill:#fdf6de" /><polygon points="602.18 602.95 578.92 562.25 578.92 562.25 602.18 562.25 602.18 602.95" stM
yle="fill:#fdf6de" /><polygon points="677.77 664 619.63 562.25 619.63 562.25 677.77 562.25 677.77 664" style="fill:#fdf6de" /><polygon points="706.01 591.32 689.4 562.25 689.4 562.25 706.01 562.25 706.01 591.32" style="fill:#fdf6de" /><path d="M443.22,676.13a61.06,61.06,0,1,1,122.11,0" transform="translate(-1.66 0)" style="fill:#e63580" /></g><g id="Eyebrows-1">undefined</g><g id="Glasses-4">undefined</g><g id="Eyes-9"><polyline points="406.74 531.45 329.95 454.66 406.06 454.66" style="fill:#e1d4d1" /><polyline poiM
nts="609.46 531.45 686.25 454.66 609.06 454.66" style="fill:#aea4b2" /><path d="M773.72,413a85.24,85.24,0,1,0-170.48,0" transform="translate(0.34)" style="fill:#959acc" /><circle cx="668.36" cy="413.04" r="64.79" style="fill:#727fbd" /><circle cx="667.27" cy="413.04" r="25.85" style="fill:#213f7d" /><polyline points="648.81 394.58 611.88 424.12 648.81 424.12" style="fill:#213f7d" /><circle cx="667.27" cy="413.04" r="18.47" style="fill:#131640" /><path d="M581.29,429.05V368.69A103.31,103.31,0,0,1,684.6,265.38h19.45"M
 transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;opacity:0.49;mix-blend-mode:overlay" /><path d="M497.89,213.26" transform="translate(0.34)" style="fill:#f1f5fa" /><path d="M241.8,413a85.25,85.25,0,0,1,170.49,0" transform="translate(0.34)" style="fill:#d0bac3" /><circle cx="347.84" cy="413.04" r="64.79" style="fill:#aaaecc" /><polyline points="365.88 394.58 402.81 424.12 365.88 424.12" style="fill:#727fbd" /><circle cx="347.41" cy="413.04" r="25.85" style="fill:M
#727fbd" /><circle cx="347.41" cy="413.04" r="18.47" style="fill:#213f7d" /><circle cx="339.93" cy="409.35" r="4.39" style="fill:#eff3f9" /><path d="M431.26,427.83V367.47A103.3,103.3,0,0,0,328,264.16H308.51" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;mix-blend-mode:overlay" /><path d="M253.74,412.68a78.86,78.86,0,1,1,157.71,0" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;mix-blend-mode:overlay" /><patM
h d="M268.9,413.43a70.69,70.69,0,1,1,141.37,0" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;mix-blend-mode:overlay" /><path d="M296.55,413.15a57.12,57.12,0,0,1,114.24,0" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;opacity:0.58;mix-blend-mode:overlay" /><path d="M760.73,413.67a78.86,78.86,0,0,0-157.72,0" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:1M
0;opacity:0.49;mix-blend-mode:overlay" /><path d="M745.56,414.42a70.69,70.69,0,0,0-141.37,0" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;opacity:0.49;mix-blend-mode:overlay" /><path d="M717.91,414.15a57.12,57.12,0,1,0-114.24,0" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;opacity:0.49;mix-blend-mode:overlay" /><path d="M572.2,428V363.34A110.66,110.66,0,0,1,682.87,252.68h20.84" transform="translate(0.34M
)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;opacity:0.49;mix-blend-mode:overlay" /><path d="M552.15,428.56V354.18A127.29,127.29,0,0,1,679.43,226.9h24" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;opacity:0.49;mix-blend-mode:overlay" /><path d="M440.4,427.44V362.77A110.65,110.65,0,0,0,329.74,252.11H308.9" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;mix-blend-mode:overlayM
" /><path d="M460.46,428V353.61A127.29,127.29,0,0,0,333.18,226.33h-24" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;mix-blend-mode:overlay" /><circle cx="659.4" cy="409.28" r="4.39" style="fill:#eff3f9" /></g><g id="Nose-1"><path d="M515.59,360.78h0A111.68,111.68,0,0,1,627.27,472.46V598.54a0,0,0,0,1,0,0H403.92a0,0,0,0,1,0,0V472.46A111.68,111.68,0,0,1,515.59,360.78Z" style="fill:#883a62;opacity:0.7000000000000001" /><path d="M458.09,527.5h59.68V427.43H476.51a2M
4.44,24.44,0,0,0-23,16.17l-18.42,51.17A24.45,24.45,0,0,0,458.09,527.5Z" transform="translate(9.03 13)" style="fill:#fa8cb2" /><circle cx="479.97" cy="487.27" r="18.01" style="fill:#542f5f" /><path d="M551.83,527.9H506.16V427.83h27.25a24.45,24.45,0,0,1,23,16.17l18.42,51.17A24.45,24.45,0,0,1,551.83,527.9Z" transform="translate(9.03 13)" style="fill:#f46461" /><circle cx="548.01" cy="486.55" r="18.01" style="fill:#172027" /></g><g id="Hat-3"><rect x="362" y="88" width="144" height="135" style="fill:#542f5f" /><rect x=L
"506" y="88" width="144" height="135" style="fill:#172027" /><rect x="254" y="214" width="513" height="18" style="fill:#172027" /></g></g></svg>h!
**************************************************
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
!22222222222222222222222222222222222222222222222222
filter: 0; jpegRotation: 90; fileterIntensity: 0.000000; filterMask: 0; module:1facing:0;
touch: (-1.0, -1.0);
AI_Scene: (-1, -1);
filter: 0; jpegRotation: 90; fileterInteM
nsity: 0.000000; filterMask: 0; module:1facing:0;
touch: (-1.0, -1.0);
AI_Scene: (-1, -1);
!22222222222222222222222222222222222222222222222222
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:GIMP="http://www.gimp.org/xmp/" xmlns:tiff="http://ns.adobe.com/tiff/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1M
.0/" xmpMM:DocumentID="gimp:docid:gimp:8316bccc-2935-4a47-97f1-f21f2f7511e8" xmpMM:InstanceID="xmp.iid:28ad7955-1d0f-4fbc-bcb9-31bd60dd7812" xmpMM:OriginalDocumentID="xmp.did:55268173-c20b-48e2-b1f8-9017bbc1123a" dc:Format="image/webp" GIMP:API="2.0" GIMP:Platform="Windows" GIMP:TimeStamp="1675232775611897" GIMP:Version="2.10.30" tiff:Orientation="1" xmp:CreatorTool="GIMP 2.10"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="saved" stEvt:changed="/" stEvt:instanceID="xmp.iid:82f338de-3b92-418b-9fe3-827fdfb9b654" sM
tEvt:softwareAgent="Gimp 2.10 (Windows)" stEvt:when="2023-02-01T07:19:57"/> <rdf:li stEvt:action="saved" stEvt:changed="/" stEvt:instanceID="xmp.iid:ffbb490b-2e1f-4668-8b2d-a092335f6f8d" stEvt:softwareAgent="Gimp 2.10 (Windows)" stEvt:when="2023-02-01T07:26:15"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                          M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        MB
                                                                                                                                                                                                                                                                                                               <?xpacket end="w"?>h!
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
4j2DC-L5:wyJzxVNyQCr40trKQw5W+FNsq4WPDqI0o0uMj0OLIoY=
9j7+:BTC/BTC::bc1q3f787hr38pmal87yxtpq8tng09q60ljjqqd759:0
IjG=:BNB.TWT-8C2:bnb12w6plevt6kmjdhawzea0u8gjll2nuser5p5e0x:242784547:te:0
c/Foundry USA Pool #dropgold/
FjDOUT:3FD037F22CE424042BB411720B98286FAC6E020A83E9D6013978D2F2C8B72620
IjGREFUND:024EE60586980A9C6377B66335EDB5E4D2E7FD700B153B658005BF01FDFFC9BA
CjA=:ETH.ETH:0x09eeC9795f09342b8E364A5ed90B7B58057576b2:2591762:te:0
FjDOUT:9933C1878C9F90EA05ED40599FEF03C94BDD2FBE57720665F193456264205014
FjDOUT:0D13B6AAB253FB9D8D26EC64A3B53817EC43602D04D3A3BB1762FAA251597257
FjDOUT:0E16416FA49D4A73F48A3F1F01E703F8B9940194A6E2250BE15726F4EDD55F3B
FjDOUT:3BE8B6600F3DE4BBC31860CB7F50D1EC38B01E0C2DFF2B576D56411345C83D8C
FjDOUT:B1ED19234C9A42E00462A84F8BA59E748DC0312EDB91142B3F018BE40D7F988C
Adobe Photoshop 21.0 (Windows)
cropWhenPrintingbool
http://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-M
c148 79.164036, 2019/08/13-01:06:57        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-M
01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:07:24+03:00" xmp:ModifyDate="2023-02-03T00:07:24+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:10302117-c060-314b-a620-8d00379ec6e5" xmpMM:DocumentID="adobe:docid:photoshop:d9ba050b-ede3-cb49-9a2a-3b3b4140153f" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-M
7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:9d78ffab-bc21-5740-a28e-1a9bdf78970f" stEvt:when="2023-02-03T00:07:24+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rM
df:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:10302117-c060-314b-a620-8d00379ec6e5" stEvt:when="2023-02-03T00:07:24+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:9d78ffab-bc21-5740-a28e-1aM
9bdf78970f" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                      M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                   <?xpacket end="w"?>
Copyright (c) 1998 Hewlett-Packard Company
IEC http://www.iec.ch
IEC http://www.iec.ch
.IEC 61966-2.1 Default RGB colour space - sRGB
.IEC 61966-2.1 Default RGB colour space - sRGB
,Reference Viewing Condition in IEC61966-2.1
,Reference Viewing Condition in IEC61966-2.1
Adobe Photoshop 21.0 (Windows)
cropWhenPrintingbool
http://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:DescriptioM
n rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:05:24+03:00" xmp:ModifyDate="2023-02-03T00:05:24+03:00" dc:format="image/M
jpeg" xmpMM:InstanceID="xmp.iid:dcbbd076-90e3-714a-ad17-5e3aa8c29009" xmpMM:DocumentID="adobe:docid:photoshop:4b629e50-f7c3-b24e-9a19-ee62c555bbac" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:actioM
n="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:0104d6c5-35f3-0e45-a87d-5ffe488fae0f" stEvt:when="2023-02-03T00:05:24+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="deM
rived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:dcbbd076-90e3-714a-ad17-5e3aa8c29009" stEvt:when="2023-02-03T00:05:24+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:0104d6c5-35f3-0e45-a87d-5ffe488fae0f" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:865M
3de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                                                                                                                                                     M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                    <?xpacket end="w"?>
Copyright (c) 1998 Hewlett-Packard Company
IEC http://www.iec.ch
IEC http://www.iec.ch
.IEC 61966-2.1 Default RGB colour space - sRGB
.IEC 61966-2.1 Default RGB colour space - sRGB
,Reference Viewing Condition in IEC61966-2.1
,Reference Viewing Condition in IEC61966-2.1
Adobe Photoshop 21.0 (Windows)
cropWhenPrintingbool
http://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57        "> <rdf:RDF xmlns:rdf="http:/M
/www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:06:11+03:00" xmp:MM
odifyDate="2023-02-03T00:06:11+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:87f68be3-dd4b-2349-adc6-93cb6ff19d58" xmpMM:DocumentID="adobe:docid:photoshop:ab05481d-a4ab-ec48-9379-8b5f15ced756" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgentM
="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:8a22ee1f-0bb9-a544-a28a-666c77beb644" stEvt:when="2023-02-03T00:06:11+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adM
obe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:87f68be3-dd4b-2349-adc6-93cb6ff19d58" stEvt:when="2023-02-03T00:06:11+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:8a22ee1f-0bb9-a544-a28a-666c77beb644" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-8M
7e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                                                                                              M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                           <?xpacket end="w"?>
Copyright (c) 1998 Hewlett-Packard Company
IEC http://www.iec.ch
IEC http://www.iec.ch
.IEC 61966-2.1 Default RGB colour space - sRGB
.IEC 61966-2.1 Default RGB colour space - sRGB
,Reference Viewing Condition in IEC61966-2.1
,Reference Viewing Condition in IEC61966-2.1
Adobe Photoshop 21.0 (Windows)
cropWhenPrintingbool
http://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.coM
m/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:03:36+03:00" xmp:ModifyDate="2023-02-03T00:03:36+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:ed709d07-309M
4-8241-b761-b5291cf565ed" xmpMM:DocumentID="adobe:docid:photoshop:efe1b116-b36e-144a-9454-8539f97dba60" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496M
-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:e96dedf3-4cfd-9448-8f08-a627edb36972" stEvt:when="2023-02-03T00:03:36+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from applM
ication/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:ed709d07-3094-8241-b761-b5291cf565ed" stEvt:when="2023-02-03T00:03:36+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:e96dedf3-4cfd-9448-8f08-a627edb36972" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photosM
hop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                                                                                                                                                                                                 M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                        <?xpacket end="w"?>
yright (c) 1998 Hewlett-Packard Company
IEC http://www.iec.ch
IEC http://www.iec.ch
.IEC 61966-2.1 Default RGB colour space - sRGB
.IEC 61966-2.1 Default RGB colour space - sRGB
iewing Condition in IEC61966-2.1
,Reference Viewing Condition in IEC61966-2.1
FjDOUT:13151B37BB35BB9F7A38D91D4216928C060DF7EBD638F840A04405BE5D6D0907
FjDOUT:4080AFC2008608D6852DB07BC067C4FCD027C8FBE4DEDBBA764333FD5DB2976B
c/Foundry USA Pool #dropgold/
Adobe Photoshop 21.0 (Windows)
cropWhenPrintingbool
http://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57        "> <M
rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02M
-03T00:03:04+03:00" xmp:ModifyDate="2023-02-03T00:03:04+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:18905f51-745a-4c47-8460-219f1d59abe4" xmpMM:DocumentID="adobe:docid:photoshop:596e5cf2-93e5-3440-b90a-44c991b060c2" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+0M
3:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:9cf81fec-b00c-b941-8732-c91540fb6ec2" stEvt:when="2023-02-03T00:03:04+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parametersM
="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:18905f51-745a-4c47-8460-219f1d59abe4" stEvt:when="2023-02-03T00:03:04+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:9cf81fec-b00c-b941-8732-c91540fb6ec2" stRef:documentID="adobe:docid:photoM
shop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                                                                     M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                    <?xpacket end="w"?>
Copyright (c) 1998 Hewlett-Packard Company
IEC http://www.iec.ch
IEC http://www.iec.ch
.IEC 61966-2.1 Default RM
GB colour space - sRGB
.IEC 61966-2.1 Default RGB colour space - sRGB
,Reference Viewing Condition in IEC61966-2.1
,Reference Viewing Condition in IEC61966-2.1
DjB=:BNB.BNB:bnb1xehn2r9dhlac5x2q74ur046nknyr2cv9hnckdn:17881693:te:0
Ahttp://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.5.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" photoshop:ColorMode="3" photoshop:M
ICCProfile="sRGB IEC61966-2.1" xmp:ModifyDate="2023-02-02T18:27:45-05:00" xmp:MetadataDate="2023-02-02T18:27:45-05:00"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="produced" stEvt:softwareAgent="Affinity Photo 1.10.6" stEvt:when="2023-02-02T18:27:45-05:00"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                           M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                              <?xpacket end="w"?>
 .)10.)-,3:J>36F7,-@WAFLNRSR2M
&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
Ahttp://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.5.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" photoshop:ColorMode="3" photoshop:M
ICCProfile="sRGB IEC61966-2.1" xmp:ModifyDate="2023-02-02T18:24:24-05:00" xmp:MetadataDate="2023-02-02T18:24:24-05:00"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="produced" stEvt:softwareAgent="Affinity Photo 1.10.6" stEvt:when="2023-02-02T18:24:24-05:00"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                           M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                              <?xpacket end="w"?>
 .)10.)-,3:J>36F7,-@WAFLNRSR2M
&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
zTXtRaw profile type exif
    "title": "The Basics",
    "description": "Basic HTML and JS Game Collection",
      "title": "Snake",
      "inscription": "6edf80efbbae537b554340c31496439b57bef65357a57f21cbb547bc6287d7bfi0"
      "title": "Tetris",
      "inscription": "d7f1917503abc4ee718e871acdd3a701cd383c9b8694692d587eebba434fb1c7i0"
      "title": "Frogger",
      "inscription": "d3c366b3605b1a212b7e648446eba21215dbdb826fd20a5dc51b7421989c35e0i0"
      "inscription": "c2b3eec36b0949dee2729265b8aa045d0dd8b24913847fb69e04946b28e68bc2i0"
      "title": "Sokoban",
      "inscription": "0eb7edf9d10e27f443d605c3fc986564df3c4914bb33cbaeabb114f5adbd47a9i0"
      "title": "Puzzle Bobble",
      "inscription": "64aba1641a97ec70ccd21bb9e99647cae7619900ad173b2d52214754bd9c0a9ei0"
      "title": "Bomberman",
      "inscription": "c10adaa4ee52306c8db381da3e136189192fab9c160de3e9f6c56b918afec141i0"
      "inscription": "6cc328043c8e8837a421e2b869026363010fb10a5e50f89005187cadc429a821i0"
      "title": "Missile Command",
      "inscription": "6a3e2377103969dedcdd58fa3d55a31901e6359e8446d97700a6e0a81b71bd24i0"
      "title": "Doodle Jump",
      "inscription": "f07e36f8d23758372d076ce17019a784b72046612fd19842d45a1182d382c3d8i0"
text/plain;charset=utf-8
/ViaBTC/Mined by nextone2/,
KjISWAPTX:0xda0464b36aacb6955fa7c1f9732b93e81d5de4d6548c22f3eabbc3a24d019dab
IjGREFUND:F7E0A1C6FD328DC8ACD55F4AA0F6D1036FF7DB0F5E71D6A352FE24B697F43189
EjC=:BNB.ETH-1C9:bnb1krqpk0d4t33uqkrajjfvgx8785tsz76axvgc8l:21290:te:0
Mined by AntPool958[
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
'#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC
 C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
",+7!!,ZZ`^^b/.<XX]
-;=O''8(%%TTYbbe**<<<I/0B328DEU23D423efl@ANXVOTRKEI]?AT1($|.
6&OMFNThRTbWaz*,Bpry
c/Foundry USA Pool #dropgold/
Bj@1fb878ce0ca08b1a4b8ba2e88b459710bf5afa2cfae0a88010466a6185159572
EjCs:LTC.LTC:ltc1qq6qse2etz6nk6ld3gmmdggwyydv37504fzt7rg:97473793:ss:0
Adobe Photoshop 21.0 (Windows)
cropWhenPrintingbool
http://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmM
eta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorToM
ol="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:05:13+03:00" xmp:ModifyDate="2023-02-03T00:05:13+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:1c80af2b-cce7-8d42-a1d6-e9008a86e4a4" xmpMM:DocumentID="adobe:docid:photoshop:39b59a96-c41e-f441-94f6-0f310e0bab75" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="crM
eated" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:7c566773-5e1c-374f-a2fe-76c79f68ef57" stEvt:when="2023-02-03T00:05:13+03:00" stEvt:softwareAgenM
t="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:1c80af2b-cce7-8d42-a1d6-e9008a86e4a4" stEvt:when="2023-02-03T00:05:13+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedM
From stRef:instanceID="xmp.iid:7c566773-5e1c-374f-a2fe-76c79f68ef57" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                             M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                            <?xpacket end="w"?>
Copyright (c) 1998 Hewlett-Packard Company
IEC http://www.iec.ch
IEC http://www.iec.ch
.IEC 61966-2.1 Default RGB colour space - sRGB
.IEC 61966-2.1 Default RGB colour space - sRGB
,Reference Viewing Condition in IEC61966-2.1
,Reference Viewing Condition in IEC61966-2.1
CjA=:BNB.BNB:bnb1xehn2r9dhlac5x2q74ur046nknyr2cv9hnckdn:3072145:te:0
Adobe Photoshop 21.0 (Windows)
cropWhenPrintingbool
http://ns.adobe.com/xap/1.0/
" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:M
stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:03:19+03:00" xmp:ModifyDate="2023-02-03T00:03:19+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:cd82dfed-a484-bc4f-a105-fab2d276973b" xmpMM:DocumentID="adobe:docid:photoshop:851dbb5d-b5ce-924e-9f02-86c32bad17ef" xmpMM
M:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21M
.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:ef2a2553-7376-db40-881f-d277a7ae9815" stEvt:when="2023-02-03T00:03:19+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:cd82dfedM
-a484-bc4f-a105-fab2d276973b" stEvt:when="2023-02-03T00:03:19+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:ef2a2553-7376-db40-881f-d277a7ae9815" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DoM
cumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta>                                                                                                                                                                                                                                                                                                                                                                                                                                                                             M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        M
                            <?xpacket end="w"?>
Copyright (c) 1998 Hewlett-Packard Company
IEC http://www.iec.ch
IEC http://www.iec.ch
.IEC 61966-2.1 Default RGB colour space - sRGB
.IEC 61966-2.1 Default RGB colour space - sRGB
,Reference Viewing Condition in IEC61966-2.1
,Reference Viewing Condition in IEC61966-2.1
KjISWAPTX:0xc2db3fe8bfc51988dc335a013c970910d91e65f74f68b7287aa6354afc971918Q
FjDOUT:8DB2E32CCAFE708B8A7EC5CF2B11D2A6B851B50CA18864A39B8081EFF69F69B9
FjDOUT:6DA7FBB369E6058B34DA69053488363904660F2FFDDA269D0E1CA0AC62E352E2
FjDOUT:FA5D8703AA6632BD34974F158F4640DE09E3D05332E3ED5DD5A58727EA17A953
LjJ=:ETH.USDC-B48:0x86a680ee2a0C4eed78664931AC9aeFD40E7575a8:195169989017:xdf
LjJ=:ETH.USDC-B48:0x86a680ee2a0C4eed78664931AC9aeFD40E7575a8:112509347596:xdf(}
text/plain;charset=utf-8
THE TRAGEDY OF ROMEO AND JULIET
by William Shakespeare
Scene I. A public place.
Scene III. Room in Capulet
Scene V. A Hall in Capulet
Scene I. An open place adjoining Capulet
Scene III. Friar Lawrence
Scene VI. Friar Lawrence
Scene I. A public Place.
ene II. A Room in Capulet
Scene III. Friar Lawrence
Scene IV. A Room in Capulet
Scene V. An open Gallery to Juliet
s Chamber, overlooking the Garden.
Scene I. Friar Lawrence
Scene II. Hall in Capulet
Scene IV. Hall in Capulet
s Chamber; Juliet on the bed.
Scene I. Mantua. A Street.
Scene II. Friar Lawrence
Scene III. A churchyard; in it a Monument belonging tM
ESCALUS, Prince of Verona.
MERCUTIO, kinsman to the Prince, and friend to Romeo.
PARIS, a young Nobleman, kinsman to the Prince.
MONTAGUE, head of a Veronese family at feud with the Capulets.
LADY MONTAGUE, wife to Montague.
ROMEO, son to Montague.
BENVOLIO, nephew to Montague, and friend to Romeo.
ABRAM, servant to Montague.
BALTHASAR, servant to Romeo.
CAPULET, head of a Veronese family at feud with the Montagues.
LADY CAPULET, wife to CM
JULIET, daughter to Capulet.
TYBALT, nephew to Lady Capulet.
S COUSIN, an old man.
PETER, servant to Juliet
SAMPSON, servant to Capulet.
GREGORY, servant to Capulet.
FRIAR LAWRENCE, a Franciscan.
FRIAR JOHN, of the same Order.
Citizens of Verona; several Men and Women, relations to both houses;
Maskers, Guards, Watchmen and Attendants.
SCENE. During the greater part of the Play in Verona;M
Fifth Act, at Mantua.
Two households, both alike in dignity,
In fair Verona, where we lay our scene,
From ancient grudge break to new mutiny,
Where civil blood makes civil hands unclean.
From forth the fatal loins of these two foes
A pair of star-cross
d lovers take their life;
d piteous overthrows
Doth with their death bury their parents
The fearful passage of their death-mark
And the continuance of tM
Which, but their children
s end, nought could remove,
Is now the two hours
 traffic of our stage;
The which, if you with patient ears attend,
What here shall miss, our toil shall strive to mend.
SCENE I. A public place.
 Enter Sampson and Gregory armed with swords and bucklers.
Gregory, on my word, we
No, for then we should be colliers.
I mean, if we be in choler, we
, while you live, draw your neck out o
I strike quickly, being moved.
But thou art not quickly moved to strike.
A dog of the house of Montague moves me.
To move is to stir; and to be valiant is to stand: therefore, if thou
art moved, thou runn
A dog of that house shall move me to stand.
I will take the wall of any man or maid of Montague
That shows thee a weak slave, for the weakest goes to the wall.
True, and therefore women, being the weaker vessels, are ever thrust to
the wall: therefore I will push Montague
s men from the wall, and
thrust his maids to the wall.
The quarrel is between our masters and us their men.
Tis all one, I will show myself a tyrant: when I have fought with the
men I will be civil with the maids, I will cut off their heads.
The heads of the maids?
Ay, the heads of the maids, or their maidenheads; take it in what sense
They must take it in sense that feel it.
Me they shall feel while I am able to stand: and
pretty piece of flesh.
Tis well thou art not fish; if thou hadst, thou hadst been poor John.
Draw thy tool; here comes of the house of Montagues.
 Enter Abram and Balthasar.
My naked weapon is out: quarrel, I will back thee.
How? Turn thy back and run?
No, marry; I fear thee!
Let us take the law of our sides; let them begin.
I will frown as I pass by, and let them take it as they list.
Nay, as they dare. I will bite my thumb at them, which is disgrace to
them if they bear it.
Do you bite your thumb at us, sir?
I do bite my thumb, sir.
Do you bite your thumb at us, sir?
Is the law of our side if I say ay?
No sir, I do not bite my thumb at you, sir; but I bite my thumb, sir.
Do you quarrel, sir?
Quarrel, sir? No, sir.
But if you do, sir, I am for you. I serve as good a man as you.
Say better; here comes one of my master
Draw, if you be men. Gregory, remember thy washing blow.
Part, fools! put up your swords, you know not what you do.
 [_Beats down their swords._]
What, art thou drawn among these heartless hinds?
Turn thee Benvolio, look upon thy death.
I do but keep the peace, put up thy sword,
Or manage it to part these men with me.
What, drawn, and talk of peace? I hate the word
As I hate hell, all Montagues, and thee:
Have at thee, coward.
 Enter three or four Citizens with clubs.
Clubs, bills and partisans! Strike! Beat them down!
Down with the Capulets! Down with the MonM
 Enter Capulet in his gown, and Lady Capulet.
What noise is this? Give me my long sword, ho!
A crutch, a crutch! Why call you for a sword?
My sword, I say! Old Montague is come,
And flourishes his blade in spite of me.
 Enter Montague and his Lady Montague.
Thou villain Capulet! Hold me not, let me go.
Thou shalt not stir one foot to seek a foe.
 Enter Prince Escalus, with Attendants.
Rebellious subjects, enemM
Profaners of this neighbour-stained steel,
Will they not hear? What, ho! You men, you beasts,
That quench the fire of your pernicious rage
With purple fountains issuing from your veins,
On pain of torture, from those bloody hands
Throw your mistemper
d weapons to the ground
And hear the sentence of your moved prince.
Three civil brawls, bred of an airy word,
By thee, old Capulet, and Montague,
d the quiet of our streets,
by their grave beseeming ornaments,
To wield old partisans, in hands as old,
d with peace, to part your canker
If ever you disturb our streets again,
Your lives shall pay the forfeit of the peace.
For this time all the rest depart away:
You, Capulet, shall go along with me,
And Montague, come you this afternoon,
To know our farther pleasure in this case,
To old Free-town, our common judgement-place.
Once more, on pain of death, all men depart.
 [_Exeunt Prince and Attendants; CapuletM
, Lady Capulet, Tybalt,
 Citizens and Servants._]
Who set this ancient quarrel new abroach?
Speak, nephew, were you by when it began?
Here were the servants of your adversary
And yours, close fighting ere I did approach.
I drew to part them, in the instant came
The fiery Tybalt, with his sword prepar
d defiance to my ears,
He swung about his head, and cut the winds,
Who nothing hurt withal, hiss
While we were interchanging thrusts andM
Came more and more, and fought on part and part,
Till the Prince came, who parted either part.
O where is Romeo, saw you him today?
Right glad I am he was not at this fray.
Madam, an hour before the worshipp
d forth the golden window of the east,
A troubled mind drave me to walk abroad,
Where underneath the grove of sycamore
That westward rooteth from this city side,
So early walking did I see your son.
Towards him I made, but he was ware of me,
tole into the covert of the wood.
I, measuring his affections by my own,
Which then most sought where most might not be found,
Being one too many by my weary self,
d my humour, not pursuing his,
d who gladly fled from me.
Many a morning hath he there been seen,
With tears augmenting the fresh morning
Adding to clouds more clouds with his deep sighs;
But all so soon as the all-cheering sun
Should in the farthest east begin to draw
The shady curtains from AuM
Away from light steals home my heavy son,
And private in his chamber pens himself,
Shuts up his windows, locks fair daylight out
And makes himself an artificial night.
Black and portentous must this humour prove,
Unless good counsel may the cause remove.
My noble uncle, do you know the cause?
I neither know it nor can learn of him.
Both by myself and many other friends;
But he, his own affections
I will not say how true
But to himself so secret and so close,
So far from sounding and discovery,
As is the bud bit with an envious worm
Ere he can spread his sweet leaves to the air,
Or dedicate his beauty to the sun.
Could we but learn from whence his sorrows grow,
We would as willingly give cure as know.
See, where he comes. So please you step aside;
ll know his grievance or be much denied.
I would thou wert so happy by thyM
To hear true shrift. Come, madam, let
 [_Exeunt Montague and Lady Montague._]
Good morrow, cousin.
Is the day so young?
But new struck nine.
Ay me, sad hours seem long.
Was that my father that went hence so fast?
It was. What sadness lengthens Romeo
Not having that which, having, makes them short.
Out of her favour where I am in love.
Alas that love so gentle in his view,
Should be so tyrannous and rough in proof.
Alas that love, whose view is muffled still,
Should, without eyes, see pathways to his will!
Where shall we dine? O me! What fray was here?
Yet tell me not, for I have heard it all.
s much to do with hate, but more with love:
Why, then, O brawling love! O loving hate!
O anything, of nothing first create!
O heavy lightness! serious vanity!
Misshapen chaos of well-seeming forms!
Feather of lead, briM
ght smoke, cold fire, sick health!
Still-waking sleep, that is not what it is!
This love feel I, that feel no love in this.
Dost thou not laugh?
No coz, I rather weep.
Good heart, at what?
Griefs of mine own lie heavy in my breast,
Which thou wilt propagate to have it prest
With more of thine. This love that thou hast shown
Doth add more grief to too much of mine own.
Love is a smoke made M
with the fume of sighs;
d, a fire sparkling in lovers
What is it else? A madness most discreet,
A choking gall, and a preserving sweet.
Soft! I will go along:
And if you leave me so, you do me wrong.
Tut! I have lost myself; I am not here.
This is not Romeo, he
Tell me in sadness who is that you love?
What, shall I groan and tellM
Groan! Why, no; but sadly tell me who.
Bid a sick man in sadness make his will,
d to one that is so ill.
In sadness, cousin, I do love a woman.
d so near when I suppos
A right good markman, and she
A right fair mark, fair coz, is soonest hit.
Well, in that hit you miss: she
s arrow, she hath Dian
And in strong proof of chastity well arM
s weak childish bow she lives uncharm
She will not stay the siege of loving terms
encounter of assailing eyes,
Nor ope her lap to saint-seducing gold:
s rich in beauty, only poor
That when she dies, with beauty dies her store.
Then she hath sworn that she will still live chaste?
She hath, and in that sparing makes huge waste;
d with her severity,
Cuts beauty off from all posterity.
She is too fair, too wise; wisely toM
To merit bliss by making me despair.
She hath forsworn to love, and in that vow
Do I live dead, that live to tell it now.
d by me, forget to think of her.
O teach me how I should forget to think.
By giving liberty unto thine eyes;
Examine other beauties.
To call hers, exquisite, in question more.
These happy masks that kiss fair ladies
Being black, puts us in mind they hide the fair;
He that is strucken blind cannot M
The precious treasure of his eyesight lost.
Show me a mistress that is passing fair,
What doth her beauty serve but as a note
Where I may read who pass
d that passing fair?
Farewell, thou canst not teach me to forget.
ll pay that doctrine, or else die in debt.
 Enter Capulet, Paris and Servant.
But Montague is bound as well as I,
In penalty alike; and
tis not hard, I think,
For men so old as we to keep the peace.
Of honourable reckoning are you both,
But now my lord, what say you to my suit?
er what I have said before.
My child is yet a stranger in the world,
She hath not seen the change of fourteen years;
Let two more summers wither in their pride
Ere we may think her ripe to be a bride.
Younger than she are happy mothers made.
d are those so early made.
The earth hath swallowed all my hopes but she,
She is the hopeful lady of my earth:
But woo her, gentle Paris, get her heart,
My will to her consent is but a part;
And she agree, within her scope of choice
Lies my consent and fair according voice.
This night I hold an old accustom
Whereto I have invited many a guest,
Such as I love, and you among the store,
One more, most welcome, makes my number more.
At my poor house look to behold this night
Earth-treading stars that make dark heaven light:
Such comfort as do lusty young men feel
Of limping winter treads, even such delight
Among fresh female buds shall you this night
Inherit at my house. Hear all, all see,
And like her most whose merit most shall be:
Which, on more view of many, mine, being one,
May stand in number, though in reckoning none.
Come, go with me. Go, sirrah, trudge about
Through fair Verona; find those persons out
Whose names are written there, [_gives a paper_] and to them say,
My house and welcome on their pleasure stay.
eunt Capulet and Paris._]
Find them out whose names are written here! It is written that the
shoemaker should meddle with his yard and the tailor with his last, the
fisher with his pencil, and the painter with his nets; but I am sent to
find those persons whose names are here writ, and can never find what
names the writing person hath here writ. I must to the learned. In good
 Enter Benvolio and Romeo.
Tut, man, one fire burns out another
Turn giddy, and be holp by backward turning;
One desperate grief cures with another
Take thou some new infection to thy eye,
And the rank poison of the old will die.
Your plantain leaf is excellent for that.
For what, I pray thee?
For your broken shin.
Why, Romeo, art thou mad?
Not mad, but bound more than a madman is:
Shut up in prison, kept without my food,
God-den, good fellow.
 go-den. I pray, sir, can you read?
Ay, mine own fortune in my misery.
Perhaps you have learned it without book.
But I pray, can you read anything you see?
Ay, If I know the letters and the language.
Ye say honestly, rest you merry!
Stay, fellow; I can read.
 [_He reads the letter._]
_Signior Martino and his wife and daughters;
County Anselmo and his beauteous sisters;
The lady widow of Utruvio;
Signior Placentio and his loveM
Mercutio and his brother Valentine;
Mine uncle Capulet, his wife, and daughters;
My fair niece Rosaline and Livia;
Signior Valentio and his cousin Tybalt;
Lucio and the lively Helena. _
A fair assembly. [_Gives back the paper_] Whither should they come?
Indeed I should have ask
ll tell you without asking. My master is M
the great rich Capulet,
and if you be not of the house of Montagues, I pray come and crush a
cup of wine. Rest you merry.
At this same ancient feast of Capulet
Sups the fair Rosaline whom thou so lov
With all the admired beauties of Verona.
Go thither and with unattainted eye,
Compare her face with some that I shall show,
And I will make thee think thy swan a crow.
When the devout religion of mine eye
Maintains such falsehood, then turn tears to fire;
ese who, often drown
Transparent heretics, be burnt for liars.
One fairer than my love? The all-seeing sun
er saw her match since first the world begun.
Tut, you saw her fair, none else being by,
d with herself in either eye:
But in that crystal scales let there be weigh
s love against some other maid
That I will show you shining at this feast,
And she shall scant show well that now shows best.
ll go along, no such sight M
But to rejoice in splendour of my own.
SCENE III. Room in Capulet
 Enter Lady Capulet and Nurse.
s my daughter? Call her forth to me.
Now, by my maidenhead, at twelve year old,
I bade her come. What, lamb! What ladybird!
s this girl? What, Juliet!
Madam, I am here. What is your will?
. Nurse, give leave awhile,
We must talk in secret. Nurse, come back again,
Thou knowest my daughter
Faith, I can tell her age unto an hour.
ll lay fourteen of my teeth,
And yet, to my teen be it spoken, I have but four,
She is not fourteen. How long is it now
A fortnight and odd days.
Even or odd, of all days in the year,
Lammas Eve at night shall she be fourteen.
God rest all Christian souls!
Were of an age. Well, Susan is with God;
She was too good for me. But as I said,
On Lammas Eve at night shall she be fourteen;
That shall she, marry; I remember it well.
Tis since the earthquake now eleven years;
I never shall forget it
Of all the days of the year, upon that day:
For I had then laid wormwood to my dug,
Sitting in the sun under the dovehouse wall;
My lord and you werM
Nay, I do bear a brain. But as I said,
When it did taste the wormwood on the nipple
Of my dug and felt it bitter, pretty fool,
To see it tetchy, and fall out with the dug!
Shake, quoth the dovehouse:
twas no need, I trow,
And since that time it is eleven years;
For then she could stand alone; nay, by th
She could have run and waddled all about;
For even the day before she broke her brow,
And then my husband,
God be with his soul!
dost thou fall upon thy face?
Thou wilt fall backward when thou hast more wit;
Wilt thou not, Jule?
 and, by my holidame,
The pretty wretch left crying, and said
To see now how a jest shall come about.
I warrant, and I should live a thousand years,
I never should forget it.
Wilt thou not, Jule?
And, pretty fool, it stinted, and said
Enough of this; I pray thee hold thy peace.
Yes, madam, yet I cannot choM
To think it should leave crying, and say
And yet I warrant it had upon it brow
A bump as big as a young cockerel
A perilous knock, and it cried bitterly.
Thou wilt fall backward when thou comest to age;
Wilt thou not, Jule?
 it stinted, and said
And stint thou too, I pray thee, Nurse, say I.
Peace, I have done. God mark thee to his grace
Thou wast the prettiest babe that e
And I might live to see thee married once, I have my wish.
Marry, that marry is the very theme
I came to talk of. Tell me, daughter Juliet,
How stands your disposition to be married?
It is an honour that I dream not of.
An honour! Were not I thine only nurse,
I would say thou hadst suck
d wisdom from thy teat.
Well, think of marriage now: younger than you,
Here in Verona, ladies of esteem,
Are made already mothers. By my count
other much upon these years
That you are now a maid. Thus, then, in brief;
The valiant Paris seeks you for his love.
A man, young lady! Lady, such a man
s summer hath not such a flower.
s a flower, in faith a very flower.
What say you, can you love the gentleman?
This night you shall behold him at our feast;
er the volume of young Paris
And find delight writ there with beaM
Examine every married lineament,
And see how one another lends content;
d in this fair volume lies,
Find written in the margent of his eyes.
This precious book of love, this unbound lover,
To beautify him, only lacks a cover:
The fish lives in the sea; and
For fair without the fair within to hide.
s eyes doth share the glory,
That in gold clasps locks in the golden story;
So shall you share all that he doth possess,
By having him, makiM
ng yourself no less.
No less, nay bigger. Women grow by men.
Speak briefly, can you like of Paris
ll look to like, if looking liking move:
But no more deep will I endart mine eye
Than your consent gives strength to make it fly.
Madam, the guests are come, supper served up, you called, my young lady
asked for, the Nurse cursed in the pantry, and everything in extremity.
I must hence to wait, I beseech you follow straight.
Juliet, the County stays.
Go, girl, seek happy nights to happy days.
 Enter Romeo, Mercutio, Benvolio, with five or six Maskers;
 Torch-bearers and others.
What, shall this speech be spoke for our excuse?
Or shall we on without apology?
The date is out of such prolixity:
ll have no Cupid hoodwink
s painted bow of lath,
 like a crow-keeper;
Nor no without-book prologue, faintly spoke
After the prompter, for our entrance:
But let them measure us by what they will,
ll measure them a measure, and be gone.
Give me a torch, I am not for this ambling;
Being but heavy I will bear the light.
Nay, gentle Romeo, we must have you dance.
Not I, believe me, you have dancing shoes,
With nimble soles, I have a soul of lead
So stakes me to the ground I cannot move.
You are a lover, borM
And soar with them above a common bound.
I am too sore enpierced with his shaft
To soar with his light feathers, and so bound,
I cannot bound a pitch above dull woe.
s heavy burden do I sink.
And, to sink in it, should you burden love;
Too great oppression for a tender thing.
Is love a tender thing? It is too rough,
Too rude, too boisterous; and it pricks like thorn.
If love be rough with you, be rough with love;
or pricking, and you beat love down.
Give me a case to put my visage in: [_Putting on a mask._]
A visor for a visor. What care I
What curious eye doth quote deformities?
Here are the beetle-brows shall blush for me.
Come, knock and enter; and no sooner in
But every man betake him to his legs.
A torch for me: let wantons, light of heart,
Tickle the senseless rushes with their heels;
d with a grandsire phrase,
ll be a candle-holder and look on,
er so fair, and I am done.
s the mouse, the constable
ll draw thee from the mire
Or save your reverence love, wherein thou stickest
Up to the ears. Come, we burn daylight, ho.
I mean sir, in delay
We waste our lights in vain, light lights by day.
Take our good meaning, for our judgment sits
Five times in that ere once in our five wits.
And we mean well in going to this mask;
I dreamt a dream tonight.
Well what was yours?
That dreamers often lie.
In bed asleep, while they do dream things true.
O, then, I see Queen Mab hath been with you.
 midwife, and she comes
In shape no bigger than an agate-stone
On the fore-finger of an alderman,
Drawn with a team of little atomies
s noses as they lie asleep:
Her waggon-spokes made M
The cover, of the wings of grasshoppers;
Her traces, of the smallest spider
The collars, of the moonshine
s bone; the lash, of film;
Her waggoner, a small grey-coated gnat,
Not half so big as a round little worm
d from the lazy finger of a maid:
Her chariot is an empty hazelnut,
Made by the joiner squirrel or old grub,
And in this state she gallops night by night
 brains, and then they dream of love;
 knees, that dream on curtsies straight;
 fingers, who straight dream on fees;
 lips, who straight on kisses dream,
Which oft the angry Mab with blisters plagues,
Because their breaths with sweetmeats tainted are:
Sometime she gallops o
And then dreams he of smelling out a suit;
And sometime comes she with a tithe-pig
s nose as a lies asleep,
 of another benefice:
Sometime she driveth o
And then dreams he of cutting foreign throats,
Of breaches, ambuscados, Spanish blades,
Of healths five fathom deep; and then anon
Drums in his ear, at which he starts and wakes;
And, being thus frighted, swears a prayer or two,
And sleeps again. This is that very Mab
That plats the manes of horses in the night;
And bakes the elf-locks in foul sluttish hairs,
Which, once untangled, much misfortune bodes:
This is the hag, when maids lieM
That presses them, and learns them first to bear,
Making them women of good carriage:
Peace, peace, Mercutio, peace,
True, I talk of dreams,
Which are the children of an idle brain,
Begot of nothing but vain fantasy,
Which is as thin of substance as the air,
And more inconstant than the wind, who wooes
Even now the frozen bosom of the north,
d, puffs away from thence,
Turning his side to the dew-droppiM
This wind you talk of blows us from ourselves:
Supper is done, and we shall come too late.
I fear too early: for my mind misgives
Some consequence yet hanging in the stars,
Shall bitterly begin his fearful date
s revels; and expire the term
Of a despised life, clos
By some vile forfeit of untimely death.
But he that hath the steerage of my course
Direct my suit. On, lusty gentlemen!
 Musicians waiting. Enter Servants.
s Potpan, that he helps not to take away?
He shift a trencher! He scrape a trencher!
When good manners shall lie all in one or two men
Away with the join-stools, remove the court-cupboard, look to the
plate. Good thou, save me a piece of marchpane; and as thou loves me,
let the porter let in Susan Grindstone and Nell. AntonM
You are looked for and called for, asked for and sought for, in the
We cannot be here and there too. Cheerly, boys. Be brisk awhile, and
the longer liver take all.
 Enter Capulet, &c. with the Guests and Gentlewomen to the Maskers.
Welcome, gentlemen, ladies that have their toes
d with corns will have a bout with you.
Ah my mistresses, which of you all
to dance? She that makes dainty,
ll swear hath corns. Am I come near ye now?
Welcome, gentlemen! I have seen the day
That I have worn a visor, and could tell
A whispering tale in a fair lady
Such as would please;
You are welcome, gentlemen! Come, musicians, play.
A hall, a hall, give room! And foot it, girls.
 [_Music plays, and they dance._]
More light, you knaves; and turn the tables up,
And quench the fire, the room is grown too hot.
d-for sport comes well.
Nay sit, nay sit, good cousin Capulet,
For you and I are past our dancing days;
t now since last yourself and I
r Lady, thirty years.
Tis since the nuptial of Lucentio,
Come Pentecost as quickly as it will,
Some five and twenty years; and then we mask
tis more, his son is elder, sir;
Will you tell me that?
His son was but a ward two years ago.
What lady is that, which doth enrich the hand
O, she doth teach the torches to burn bright!
It seems she hangs upon the cheek of night
As a rich jewel in an Ethiop
Beauty too rich for use, for earth too dear!
So shows a snowy dove trooping with crows
er her fellows shows.
ll watch her place of stand,
g hers, make blessed my rude hand.
Did my heart love till now? Forswear it, sight!
er saw true beauty till this night.
This by his voice, should be a Montague.
Fetch me my rapier, boy. What, dares the slave
d with an antic face,
To fleer and scorn at our solemnity?
Now by the stock and honour of my kin,
To strike him dead I hold it not a sin.
Why how now, kinsman!
Wherefore storm you so?
Uncle, this is a Montague, our foe;
is hither come in spite,
To scorn at our solemnity this night.
Tis he, that villain Romeo.
Content thee, gentle coz, let him alone,
A bears him like a portly gentleman;
And, to say truth, Verona brags of him
To be a virtuous and well-govern
I would not for the wealth of all the town
Here in my house do him disparagement.
Therefore be patient, take no note of him,
It is my will; the which if thou respect,
Show a fair presence and put ofM
An ill-beseeming semblance for a feast.
It fits when such a villain is a guest:
What, goodman boy! I say he shall, go to;
Am I the master here, or you? Go to.
ll not endure him! God shall mend my soul,
ll make a mutiny among my guests!
You will set cock-a-hoop, you
You are a saucy boy. Is
This trick may chance M
to scathe you, I know what.
You must contrary me! Marry,
Well said, my hearts!
You are a princox; go:
More light, more light!
ll make you quiet. What, cheerly, my hearts.
Patience perforce with wilful choler meeting
Makes my flesh tremble in their different greeting.
I will withdraw: but this intrusion shall,
Now seeming sweet, convert to bitter gall.
[_To Juliet._] If I profane with my unworthiest hand
This holy shrine, theM
 gentle sin is this,
My lips, two blushing pilgrims, ready stand
To smooth that rough touch with a tender kiss.
Good pilgrim, you do wrong your hand too much,
Which mannerly devotion shows in this;
For saints have hands that pilgrims
And palm to palm is holy palmers
Have not saints lips, and holy palmers too?
Ay, pilgrim, lips that they must use in prayer.
O, then, dear saint, let lips do what hands do:
They pray, grant thou, lest faithM
Saints do not move, though grant for prayers
Then move not while my prayer
Thus from my lips, by thine my sin is purg
Then have my lips the sin that they have took.
Sin from my lips? O trespass sweetly urg
Give me my sin again.
You kiss by the book.
Madam, your mother craves a word with you.
Her mother is the ladyM
And a good lady, and a wise and virtuous.
d her daughter that you talk
I tell you, he that can lay hold of her
Shall have the chinks.
O dear account! My life is my foe
Away, be gone; the sport is at the best.
Ay, so I fear; the more is my unrest.
Nay, gentlemen, prepare not to be gone,
We have a trifling foolish banquet towards.
en so? Why then, I thank you all;
I thank you, honest gentM
More torches here! Come on then, let
Ah, sirrah, by my fay, it waxes late,
 [_Exeunt all but Juliet and Nurse._]
Come hither, Nurse. What is yond gentleman?
The son and heir of old Tiberio.
s he that now is going out of door?
Marry, that I think be young Petruchio.
s he that follows here, that would not dance?
Go ask his name. If he be married,
is like to be my wedding bed.
His name is Romeo, and a Montague,
The only son of your great enemy.
My only love sprung from my only hate!
Too early seen unknown, and known too late!
Prodigious birth of love it is to me,
That I must love a loathed enemy.
 [_One calls within,
s away, the strangers all are gone.
Now old desire doth in his deathbed lie,
And young affection gapes to be his heir;
That fair for which love groan
d for and would die,
With tender Juliet match
Alike bewitched by the charm of looks;
But to his foe suppos
s sweet bait from fearful hooks:
Being held a foe, he may not have access
To breathe such vows as lovers use to swear;
And she as much in loM
ve, her means much less
To meet her new beloved anywhere.
But passion lends them power, time means, to meet,
Tem